终、《图解HTTP》读书笔记 - 汇总篇(总结
引言
又一本网络基础的书啃完了,这本书建议结合[[《网络是怎么样连接的》读书笔记 - 汇总篇]]这一篇读书笔记食用(当然也可以直接看原书)。
把这两本书啃完对于整个互联网的基础脉络有一个大概的认知
在阅读这份汇总笔记之前,我们先从全局看一下大概讲了什么内容。
幕布地址:读书笔记大纲
读书笔记大纲
超长文警告!强烈建议阅读原文!
介绍
HTTP的好书《HTTP 权威指南》,以及网络编程必看书《TCP/IP 详解,卷 1》都比较难啃。我相信如果一上来就看这两本书,基本没有多少人能坚持,更多的可能是看了几页之后立马丢到一边成为显示器脚垫。
这时候应该选择降级选择更低一档的入门书,也就是这篇读书笔记对应的书籍《图解HTTP》。
这本书的优点是简单了解HTTP的神秘面纱同时,能让你坚持看下不会觉得枯燥,虽然想要成为强者必须要经过上面两本书的磨练,但是在等级不够的时候,更建议好好静心心来看这本书。
看完这本之后,可以再看看《图解TCP/IP》,算是啃黑皮书之前的最后一道内功,这本书对于OSI模型七层架构以及TCP/IP模型进一步阐述。
再次强调,没有网络基础,不要上来先看《TCP/IP 详解》,否则你会感觉在看天书(过来人的经验)。
资源链接
《图解HTTP》电子书资源链接:
链接: https://pan.baidu.com/s/1ZacpnZqfuqSaaL3meXR7aA?pwd=7om2 提取码: 7om2 如果失效可以到公众号“懒时小窝” 回复“图解HTTP”获取图书资源以及附录部分的参考资料。
章节梳理
入门章节(概览整个HTTP,了解HTTP定位)
第一章(掌握):WEB 基础和HTTP的诞生,TCP/IP协议、URL和URI的了解,如果已经很熟悉了可以跳过。
#知识点
- 概览HTTP诞生历史。文中提供一个中文翻译网站可以对照阅读。
- 扩展:HTTP3.0 都已经出来了,为什么2.0 推进还是只有一半?题外话讨论
- TCP/IP 协议概览,了解基本定义。
- 区分URL和URI。
重点章节(必看并且消化以及自我复述)
第二章(掌握):HTTP重点特性介绍。了解HTTP协议重点升级内容,理解加记忆为主。读书笔记补充了HTTPS的历史(必看)
#知识点
- 请求和响应报文的结构。
- HTTP协议进化历史,介绍不同HTTP版本从无到有的重大特性改变。(重点)
- HTTP几个比较常见的问题讨论。
第三章(掌握):HTTP报文信息。报文是WEB的核心,需要重点掌握。
#知识点
第七章(掌握):重点是SSL/TLS协议的掌握,细节比较多。
#知识点
- HTTPS 是什么?HTTP有哪些缺点?
- SSL、TLS为啥总是被放到一起,有什么区别?
- SSL、TLS历史背景。
- SSL的加密细节,加密算法了解。
- SSL的加密流程。
第六章(熟悉):介绍HTTP头部内容。不需要记忆背诵,只需要用到的时候查表即可,此外记住一些常见头部应付面试。
#知识点
1. 请求头部字段分类比较多,本章介绍了下面的首部,内容非常多,熟悉常见的请求首部即可。
1. 首部字段介绍
2. 非HTTP1.1 首部字段
3. 通用首部
4. 请求首部
5. 响应首部
6. 负载首部(实体首部)
7. 其他首部字段
2. 协作服务器指的是为了HTTP加速访问而架设的一些中间件,内容介绍比较匮乏,个人也没有补充,简单浏览即可
非重点章节(知识点模糊则建议仔细阅读)
第四章(熟悉):常见HTTP状态码介绍,虽然同样也不需要记忆,只需要用到的时候查表,但是作为开发人员,常见的一些响应码需要掌握并且注意细节。
#知识点
- 状态码定义的IETF协议规范,使用 RFC 7231 作为协议参考。
- 常见状态码定义,以及在 RFC 7231 中的协议定义参考
- 如何选择合适的状态码,这里仅介绍了 GET/POST/HEAD 三个最常用的状态码定义参考。
第八章(拓展):身份认证方式的扩展阅读。重点熟悉掌握SSL认证,细节很多。(和第七章有重合,书籍编排问题)
#知识点
- 身份认证的几种常见方式
- BASIC认证(基本认证)
- DIGEST认证(摘要认证)
- SSL客户端认证
- FormBase认证(表单认证)
- 重点介绍SSL认证细节,其他认证方式可以不看或者跳过。
- Keberos 认证和NTLM 认证,Keberos 认证是大数据身份认证的事实标准,大数据相关领域工作者有必要关注。
选看章节(非重点,大部分可以跳,小部分需了解)
第十一章(了解):RSS介绍,WEB攻击与防护。WEB攻击部分有时候面试会问到,建议了解一下。
#知识点
- RSS历史介绍,RSS的存在意义和价值,个人认为很适合自主学习的人使用。
- WEB 攻击手段介绍,了解基础和常见的攻击手段,这些攻击手段离我们日常生活并不远。
- 关于瓶颈和未来发展是由于写书时效性产生的一些“过时”内容,可以跳过。
第十章(跳过):构建WEB内容:和WEB有关搭配组件概念。也是泛泛而谈。
第九章(跳过):HTTP的瓶颈和未来发展。在写这本书的时候HTTP2.0还没出来,现在HTTP3.0都已经发展了不少了=-=,建议看看读书笔记部分的第二个大章介绍。
第五章(跳过):介绍代理、网关、隧道的大致概念。其实就是介紹了部分互联网架构组件,讲的过于浅并且编排不是很好,建议看《网络是怎么样连接的》,另外自荐一下自己的读书笔记。
附录
虽然标题起名叫“附录”,实际上是个人搜集笔记而已。
附录部分是把之前各个章节参考的各种文章和资料汇总一遍,如果你也想阅读这本书,相信这些内容对你一定有帮助。
[[《图解HTTP》读书笔记 - 附录]]
历史文章汇总
如果不喜欢又臭又长的文章,建议拆分阅读,体验更佳,本文为汇总实际上就是把这些文章合并到一起阅读。
当然不一定非按我的编排方式,建议根据自己的习惯来看即可。
入门章节
重点章节
非重点章节(知识点模糊则建议仔细阅读)
选看章节(非重点,大部分可以跳,小部分需了解)
附录
正文部分
一、《图解HTTP》- WEB和网络基础
知识点
- 概览HTTP诞生历史。文中提供一个中文翻译网站可以对照阅读。
- 扩展:HTTP3.0 都已经出来了,为什么2.0 推进还是只有一半?题外话讨论
- TCP/IP 协议概览,了解基本定义。
- 区分URL和URI。
1.1 本章重点
开头部分是关于WEB和网络历史介绍,所以没有多少需要理解和记忆的内容。网络基础 TCP/IP的部分是整个互联网的核心,建议多看几遍理解和消化。
WEB的传输依赖HTTP(HyperText Transfer Protocol,超文本传输协议 1 )的协议作为规范,HTTP的工作是完成从客户端到服务器端等一系列运作流程,为了保证两台不同的设备能正常沟通,两者需要遵守相同的规则。
目前HTTP已经发展到3.0了,但是这本书写下来的时候2.0还在起草,所以可以认为是一本比较“老”的书,很多地方需要查阅当前的资料进行纠正。
1.2 HTTP诞生
1989 年 3 月HTTP在少数几个人手中诞生。CERN(欧洲核子研究组织)的蒂姆 • 伯纳斯 - 李(Tim BernersLee)提出网络通信的设想。
1990 年 11 月,CERN 成功研发了世界上第一台 Web 服务器和 Web 浏 览器。
1990 年,大家针对 HTML 1.0 草案进行了讨论,因 HTML 1.0 中存在 多处模糊不清的部分,草案被直接废弃了。
1993 年 1 月,现代浏览器的祖先 NCSA(National Center for Supercomputer Applications,美国国家超级计算机应用中心)研发的 Mosaic 问世了。同年秋天发布Windows 版和 Macintosh 版面世。
1994 年 的 12 月,时代的眼泪网景通信公司发布了 netscape Navigator 1.0,1995 年微软公司发布臭名昭著的 Internet Explorer 1.0 和 2.0,随后IE折磨开发人员数十年的历史开始。
现在的FireFox浏览器前身就是网景,不过浏览器内核却是谷歌一家独大,Edge在chrome内核以及自身优化的加持下也可圈可点,但是微软利用平台强制绑定客户的行为依旧可见一二,这都是现代的用户可以感知的。
另外值得一提的是3W的构建包含三种技术:
- SGML(Standard Generalized MarkuP Language,标准通用标记语言),是最早的超文本格式的最高层次标准。可以定义标记语言的元语言,甚至可以定义不必采用< >的常规方式,因为太复杂因而难以普及。后续的HTTP和XML都可以看作这个协议的扩展和拆分和简化。
- HTML(HyperText MarkuP Language,超文本标记语言)。
- URL(Uniform12 Resource Locator,统一资源定位符)。
1.3 HTTP历史简述
HTTP发展到现在也基本所有网站都是HTTP1.1版本作为标准,自 1999 年发布的 RFC2616 之后发布过一个版本RFC723。
这部分内容在第二章中会再次重点扩展和讨论
**RFC7231 协议在线阅读:
https://tools.ietf.org/html/rfc7231
历史发展
感兴趣可以点击协议号查看原文。
- HTTP/0.9:HTTP 于 1990 年问世,这个版本可以看作是1.0之前的雏形,因为没有作为标准正式版建立,所以被叫做为0.9。
- HTTP/1.0:HTTP 正式作为标准被公布是在 1996 年的 5 月,标准号为RFC1945(点击链接查看白皮书)。
- HTTP/1.1:1997 年 1 月公布,直到现在依然还有大量的网站在使用,最初标准为RFC2068,之后发布的修订版 RFC2616 ,目前最新版本为rfc7231。
- HTTP/2.0:HTTP/2是HTTP协议自1999年HTTP 1.1的改进版RFC2616,发布后的首个更新,主要基于SPDY协议。(RFC 7231 时后面修订的) 它由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工作小组进行开发。该组织于2014年12月将HTTP/2标准提议递交至IESG(英语:Internet_Engineering_Steering_Group)进行讨论,于2015年2月17日被批准。 HTTP2.0的标准号:RFC 7540
中英文对照阅读
最后在网上找到一个关于HTTP协议的翻译网站,项目作者貌似弃坑很多年没有更新,但是对于英语基础较薄弱的同学可以作为参考:
rfc7540-translation-zh_cn
1.3.1 HTTP/2.0 的特点
HTTP/2.0 的目标是改善用户在使用 Web 时的速度体验。
为了支撑这一实现,官方提出了三种技术:
- SPDY(SPDY HTTP Speed):谷歌提出用于提高HTTP访问效率以及解决HTTP1.X中管道化的缺陷,意图是缩短整个请求时间。
- mobility Network-Friendly:由微软公司起草,是用于改善并提高移动端通信时的通信速度和性能的标准。见名知意它是被用来实现手机高速上网的一个协议。
- HTTP Upgrade (Network-Friendly HTTP Upgrade):同样是对于移动端的一些改进意见。
1.3.2 HTTP2.0题外话
这本书写于13,14年左右,HTTP2.0到现在都推广快接近十年了,但是推广速度个人认为一般。
作为读者肯定好奇现在到底普及多少了,这里找到一个可供参考的网站。
从纸面数据来看截止到目前国外的统计目前使用HTTP/2的还不到一半,也就意味着还有一大半的服务器还在使用HTTP1.1。
这就引申了下一个话题,3.0都快要出来了为什么2.0还没全面普及?
这就要了解为什么2.0的普及这么难。
首先是HTTP1.0中请求非常纯粹,一个请求就是一次HTTP连接,请求完成就断开。
于是 HTTP1.X 升级了一下,可以通过Keep-alive优化TCP的建立和断开,一次连接也可以对应多个请求。
然而谷歌是不会满足这样的效率的,谷歌推动了HTTP2.0的升级,针对HTTP1.X的请求响应必须要排队的问题处理,使用多路复用完成整个请求。
这样当然对于效果显著提升,为什么大家都不用呢?
为什么协议在进步,看起来效率在显著提升,为什么HTTP的升级难以推进?
表层来看
真实的项目基本需要依赖框架完成,有一些旧系统旧版本的框架不是想升级就升级的,或者说压根懒得升级,因为没有显著的带来效益的好处。甚至有可能搞出问题得不偿失。
更深层次的原因
HTTP2.0 自带HTTPS,这样实际上就导致一个冲突问题,实际的项目大多需要使用Nginx反向代理。
但是Ngnix也可以开启Http2.0的支持呀,为什么依然坚持要用Nginx 做反向代理而不是直接使用HTTP2.0呢?
这样的原因可能来自TCP长链接,在Nginx部署的情况下,实际上请求不需要走一长串的路由而是直接和Nginx交互。
但是HTTP2.0 可以通过多点部署以及多个请求顺序并行,通过集群和负载均衡可以很好的满足服务器要求。
在框架当中如果把请求发给本地,局限单台机器的核数量,并发效率实际上和HTTP1.X差不多,因为任务多起来依然需要排队。
如果开启HTTP2.0并且交给Nginx 拆分模块并且简化功能,不改变开发模式的情况下又可以利用集群。
此外,最关键的一个原因,HTTP2.0虽然解决了HTTP多路复用并发请求的问题,但是TCP的问题并没有被解决。
所以大体上是TCP的锅,其次是Nginx够强,以及框架升级的高成本问题,最后是期待HTTP3.0一步到胃。
当然不要认为50% 普及率很低,从另一个角度来看访问量很大日常使用的网站基本都有HTT P2.0的加成。
1.4 TCP/IP
理解HTTP必然需要了解TCP/IP。
HTTP协议是应用层的协议,如果是金字塔结构它处于模型的顶层,但是实际上金字塔的核心是TCP/IP。
HTTP是在此基础上做支撑的,现代的网络架构是基于TCP/IP模型建立的,HTTP也只是其中的一部分。
TCP/IP 是互联网相关的各类协议族的总称,这是一种说法,表示单单是字面意思的TCP协议和IP协议。但是另一种说法是只是代表了TCP 和 IP 这两种协议。
TCP/IP 协议族按层次分别分 为以下5 层:应用层、传输层、网络层和数据链路层,以及和硬件密切相关的物理层。
层次化的设计意味着便于修改,也就是常说的高内聚低耦合在TCP/IP
协议得到充分体现。
但是实际上这种设计不是完全没有缺点的,那就是每一层虽然可以升级但是无法突破原有的框架,TCP协议本身限制也是导致HTTP协议难以推动升级的一个重要原因。
那么《TCP/IP 详解,卷 1》中开头介绍了OSI 模型又是啥?
实际上是早期互联网协议构建者的美好愿景,企图通过这一个模型实现一个极强扩展性的互联网架构,说难听点就是把标准给完全垄断掉,让以后的架构无牌可打你们都得听我的。
当然理想和美好现实很骨感,由于OSI模型结构的层级过多并且难以推动和维护,后续被更为精简好理解的TCP/IP 快速取代。
所以OSI模型是历史斗争的产物,但是却是实际上网络模型协议制定标杆, TCP/IP 模型借助UNIX最终存活下来。
OSI模型
根据模型介绍各层都做了什么?
- 应用层:决定为用户提供服务的应用程序相关活动。
- 传输层:传输层主要是协议之间的数据传输,包含各种丰富多样的协议,包括但是不限于TCP协议,比如TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。
- 网络层:网络层用来处理在网络上流动的数据包,最终转化为网络包的最小单位进行传输。
- 数据链路层:也可以认为是看的见的硬件部分,比如网卡,硬件上的范畴均在链路层的作用范围之内。
- 物理层:也就是网线和集线器等网络传输支持设备,从粗略角度来看可以直接看作网线。
下面是整个网络数据包的封装过程,如果想要了解整个过程可以查看 《网络是怎么样连接的》这本书的第二章部分。
TCP/IP请求模型
1.4.1 IP、TCP 和DNS
按照协议层次划分:IP(Internet Protocol)网际协议位于网络层,TCP位于传输层,所以TCP/IP 协议除了代表一个协议族群之外,TCP协议和IP协议本身其实就不在一个层级,所以不能混为一谈。
IP协议(Internet Protocol)
IP(Internet Protocol)网际协议位于网络层。
IP协议的主要工作是确保信息能准确传输,为了保证数据能正确的送达,IP协议需要确保MAC地址和IP地址的正确,IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。
IP地址由于内外网通信的缘故有可能会存在地址转化,地址转化依赖的是地址转化设备,但是MAC地址是从网卡生产之后就固定了世界上唯一的网卡MAC地址。
ARP 协议凭借 MAC 地址进行通信,接着便是通过类似快递导航寻找站点的方式进行通信传输,整个核心是通过“查表”方式进行。
ARP 协议(Address Resolution Protocol):ARP 是一种用以解析地址的协议,根据通信方的 IP 地址就可以反查出对应的 MAC 地址。
请求路由
TCP协议
TCP协议位于传输层,提供字节流服务,所谓字节流服务是指大块数据拆分为报文段, 而可靠服务指的是把数据传输给对方,同时TCP为了保证大段数据的传输会进行数据切割。
为了确保数据准确传输,整个TCP还需要依赖三次握手,关于三次握手的过程这里也不做过多讨论,可以看《网络是怎么样连接的》读书笔记内容。
TCP协议
DNS 服务
用户通常使用主机名或域名来访问对方的计算机,而不是直接通过 IP地址访问。
DNS是负责域名和IP转化的服务,在请求目标服务器之前,通常需要根据DNS服务获取域名对应的IP地址。
各协议和HTTP关系
注意在书中省略有关MAC头部的内容。
整个图画算是最为入门级的角度,实际上稍微深入就会发现没有那么简单,这幅图也是画的过于笼统,简单看看角色的基本职责即可。
各协议和HTTP关系
1.4.2 URL和URI
区别和对比
首先得区分概念本身:
URL
:表示统一资源定位,也就是我们访问WEB 服务器在浏览器顶部的那一串字符串。
URI
:其实这里包含三个组件,URI全称 是 Uniform Resource Identifier,RFC2396在规范的1.1分别对于这三个单词进行下面的定义,整体来看URI 就是由某个协议方案表示的资源的定位标识符。
-
Uniform
:规定统一的格式可方便处理多种不同类型的资源,也就是常说的“习惯优于配置”,具体案例是比如ftp是ftp开头请求走协议,http开头请求走http协议。 -
Resource
:抽象定义资源是“任何可以访问的东西”,比如文档,图片,网络文件等等全都可以看做资源。 -
Identifier
:表示可以标识的对象,也叫做标识符。
最快速的区分可以直接认为 URI 是协议和标准,URL是对于URI协议标准的“直接实现”和“表示”。
URI属于互联网顶级规范的行列,只有符合URI规范的请求才能被识别,由隶属于国际互联网资源管理的非营利社团 ICANN(Internet Corporation forAssigned Names and Numbers,互联网名称与数字地址分配机构)的IANA(Internet Assigned Numbers Authority,互联网号码分配局)管理颁布。
最后是有关URL定义的两个直观例子:
hierarchical part
┌───────────────────┴─────────────────────┐
authority path
┌───────────────┴───────────────┐┌───┴────┐
abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
└┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘
scheme user @R_893_4045@ion host port query fragment
urn:example:mammal:monotreme:echidna
└┬┘ └──────────────┬───────────────┘
scheme path
当然官方在白皮书也给了一些案例:
The following examples illustrate URI that are in common use.
ftp://ftp.is.co.za/rfc/rfc1808.txt
-- ftp scheme for File Transfer Protocol services
gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles
-- gopher scheme for Gopher and Gopher+ Protocol services
http://www.math.uio.no/faq/compression-faq/part1.html
-- http scheme for Hypertext Transfer Protocol services
mailto:[email protected]
-- mailto scheme for electronic mail addresses
news:comp.infosystems.www.servers.unix
-- news scheme for USENET news groups and articles
telnet://melvyl.ucop.edu/
-- telnet scheme for interactive services via the TELNET Protocol
最后我们简单来对比URL和URI,可以看到URI划定了URL的“分类”,所以URL可以看做是URI的子集。
URL 格式
URL 格式
这里主要介绍用的比较少的片段标识符,片段标识符表示已获取资源中的子资源。
注意互联网中并不是所有的请求都会符合RFC协议的,RFC指的是HTTP协议的意见修正书,这些内容多数情况下应用程序都会遵守,但是凡事总有特例。
如果不参考RFC协议进行通信那么就需要自己的协议完成客户端之间的通信,比较典型的例子比如RPC协议就是经典的非HTTP协议通信实现,当然这种方案存在不少的问题和争议。
1.5 总结
和多数技术书类似,第一章算是概览的一个章节,大致介绍了HTTP的基础背景。
当然这本书确实很老,很多协议和标准到现在其实早就不在使用了,但是从另一个角度来看IP、TCP、DNS这些东西基本都是万年不变的,所以不需要担心会过时。
关于HTTP2.0的讨论是笔记中的扩展部分,在这一部分大致讨论了一下为什么HTTP2.0难以推进。
实际上HTTP2.0在各大主流网站都有普及,国内的一些大厂商基本也是第一时间跟进的。
二、《图解HTTP》- HTTP协议历史发展(重点)
知识点
- 请求和响应报文的结构。
- HTTP协议进化历史,介绍不同HTTP版本从无到有的重大特性改变。(重点)
- HTTP几个比较常见的问题讨论。
2.0 介绍
这一章节基本上大部分为个人扩展,因为书中的内容讲的实在是比较浅。本文内容非常长,另外哪怕这么长也只是讲到了HTTP协议的一部分而已,HTTP协议本身十分复杂。
2.1 请求和响应报文结构
请求报文的基本内容:
请求和响应报文结构
请求内容需要客户端发给服务端:
GET /index.htm HTTP/1.1
Host: hackr.jp
响应报文的基本内容:
响应报文的基本内容
服务器按照请求内容处理结果返回:
开头部分是HTTP协议版本,紧接着是状态码 200 以及原因短语。
HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 06:50:15 GMT
Content-Length: 362
Content-Type: text/html
<html> ……
2.2 HTTP进化历史
协议版本 |
解决的核心问题 |
解决方式 |
---|---|---|
0.9 |
HTML 文件传输 |
确立了客户端请求、服务端响应的通信流程 |
1.0 |
不同类型文件传输 |
设立头部字段 |
1.1 |
创建/断开 TCP 连接开销大 |
建立长连接进行复用 |
2 |
并发数有限 |
二进制分帧 |
3 |
TCP 丢包阻塞 |
采用 UDP 协议 |
SPDY |
HTTP1.X的请求延迟 |
多路复用 |
2.2.1 概览
我们复盘HTTP的进化历史,下面是抛去所有细节,整个HTTP连接大致的进化路线。
- http0.9:只具备最基础的HTTP连接模型,在非常短的一段时间内存在,后面被快速完善。
- http1.0: 1.0版本中每个TCP连接只能发送一个请求,数据发送完毕连接就关闭,如果还要请求其他资源,就必须重新建立TCP连接。(TCP为了保证正确性和可靠性需要客户端和服务器三次握手和四次挥手,因此建立连接成本很高)
- http1.1:
-
HTTP/2:
- 二进制格式:1.x是文本协议,然而2.0是以二进制帧为基本单位,可以说是一个二进制协议,将所有传输的信息分割为消息和帧,并采用二进制格式的编码,一帧中包含数据和标识符,使得网络传输变得高效而灵活;
- 多路复用:2.0版本的多路复用多个请求共用一个连接,多个请求可以同时在一个TCP连接上并发,主要借助于二进制帧中的标识进行区分实现链路的复用;
- 头部压缩:2.0版本使用使用HPACK算法对头部header数据进行压缩,从而减少请求的大小提高效率,这个非常好理解,之前每次发送都要带相同的header,显得很冗余,2.0版本对头部信息进行增量更新有效减少了头部数据的传输;
- 服务端推送:在2.0版本允许服务器主动向客户端发送资源,这样在客户端可以起到加速的作用;
- HTTP/3:
这个版本是划时代的改变,在HTTP/3中,将弃用TCP协议,改为使用基于UDP协议的QUIC协议实现。需要注意QUIC是谷歌提出的(和2.0 的SPDY 一样),QUIC指的是快速 UDP Internet 连接,既然使用了UDP,那么也意味着网络可能存在丢包和稳定性下降。谷歌当然不会让这样的事情发生,所以他们提出的QUIC既可以保证稳定性,又可以保证SSL的兼容,因为HTTP3上来就会和TLS1.3一起上线。
基于这些原因,制定网络协议IETF的人马上基本都同意了QUIC的提案(太好了又能白嫖成果),于是HTTP3.0 就这样来了。但是这只是最基本的草案,后续的讨论中希望QUIC可以兼容其他的传输协议,最终的排序如下IP / UDP / QUIC / HTTP。另外TLS有一个细节优化是在进行连接的时候浏览器第一次就把自己的密钥交换的素材发给服务器,这样进一步缩短了交换的时间。
为什么HTTP3.0要从协议根本上动刀,那是因为HTTP/2虽然解决了HTTP协议无法多路复用的问题,但是没有从TCP层面解决问题,具体的TCP问题体现如下:
-
队头阻塞,
HTTP/2
多个请求跑在一个TCP
连接中,如果此时序号较低的网络请求被阻塞,那么即使序列号较高的TCP
段已经被接收了,应用层也无法从内核中读取到这部分数据,从 HTTP 视角看就是多个请求被阻塞了,并且页面也只是加载了一部分内容; -
TCP
和TLS
握手时延缩短:TCL
三次握手和TLS
四次握手,共有3-RTT
的时延,HTPT/3最终压缩到1 RTT(难以想象有多快); -
连接迁移需要重新连接,移动设备从
4G
网络环境切换到WIFI
时,由于TCP
是基于四元组来确认一条TCP
连接的,那么网络环境变化后,就会导致IP
地址或端口变化,于是TCP
只能断开连接,然后再重新建立连接,切换网络环境的成本高;
RTT:RTT是Round Trip Time的缩写,简单来说就是通信一来一回的时间。
下面是官方对于RTT速度缩短的对比,最终只在初次连接需要1RTT的密钥交换,之后的连接均为0RTT!
0RTT
2.2.2 HTTP 0.9
这个版本基本就是草稿纸协议,但是它具备了HTTP最原始的基础模型,比如只有GET命令,没有 Header 信息,传达的目的地也十分简单,没有多重数据格式,只有最简单的文本。
此外服务器一次建立发送请求内容之后就会立马关闭TCP连接,这时候的版本一个TCP还只能发送一个HTTP请求,采用一应一答的方式。
2.2.3 HTTP 1.0
协议原文:https://datatracker.ietf.org/doc/html/rfc1945
显然HTTP 0.9缺陷非常多并且不能满足网络传输要求。浏览器现在需要传输更为复杂的图片,脚本,音频视频数据。
1996年HTTP进行了一次大升级,主要的更新如下:
在HTTP1.0 协议原文中开头有一句话:
原文:
Status of This Memo:
This memo provides @R_893_4045@ion for the Internet community. This memo
does not specify an Internet standard of any kind. distribution of
this memo is unlimited.
这份协议用了memo这个单词,memo 的意思是备忘录,也就是说虽然洋洋洒洒写了一大堆看似类似标准的规定,但是实际上还是草稿,没有规定任何的协议和标准,另外这份协议是在麻省理工的一个分校起草的,所以可以认为是讨论之后临时的一份方案。
HTTP1.0主要改动点介绍
在了解了这是一份备忘录的前提下,我们来介绍协议的一些重要概念提出。
HTTP1.0 定义了无状态、无连接的应用层协议,纸面化定义了HTTP协议本身。
无状态、无连接定义:HTTP1.0 规定服务器和客户端之间可以保持短暂连接,每次请求都需要发起一次新的TCP连接(无连接),连接完成之后立马断开连接,同时服务器不负责记录过去的请求(无状态)。
这样就出现一个问题,那就是通常一次访问需要多个HTTP请求,所以每一次请求都要建立一次TCP连接效率非常低,此外还存在两个比较严重的问题:队头阻塞和无法复用连接。
队头阻塞:因为TCP连接是类似排队的方式发送,如果前一个请求没有到达或者丢失,后一个请求就需要等待前面的请求完成或者完成重传才能进行请求。
无法复用连接:TCO连接资源本身就是有限的,同时因为TCP自身调节(滑动窗口)的关系,TCP为了防止网络拥堵会有一个慢启动的过程。
RTT时间计算:TCP三次握手共计需要至少1.5个RTT,注意是HTTP连接不是HTTPS连接。
滑动窗口:简单理解是TCP 提供一种可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量的机制。
2.2.4 HTTP1.1
HTTP 1.1 的升级改动较大,主要的改动点是解决建立连接和传输数据的问题,在HTTP1.1中引入了下面的内容进行改进:
- 长连接:也就是
Keep-alive
头部字段,让TCP默认不进行关闭,保证一个TCP可以传递多个HTTP请求 - 并发连接:一个域名允许指定多个长连接(注意如果超出上限依然阻塞);
- 管道机制:一个TCP可以同时发送多个请求(但是实际效果很鸡肋还会增加服务器压力,所以通常被禁用);
- 增加更多方法:PUT、DELETE、OPTIONS、PATCH等;
- HTTP1.0新增缓存字段(If-Modified-Since, if-none-match),而HTTP1.1则引入了更多字段,比如
Entity tag,If-Unmodified-Since, If-Match, if-none-match
等更多缓存头部的缓存策略。 - 允许数据分块传输(Chunked),对于大数据传输很重要;
- 强制使用Host头部,为互联网的主机托管创造条件;
- 请求头中引入了 支持断点续传的
range
字段;
下面为书中第二章节记录的笔记内容,写书日期是HTTP1.1蓬勃发展的时候 ,基本对应了HTTP1.1协议中的一些显著特点。
无状态协议
HTTP 协议自身不具备保存之前发送过的请求或响应的功能,换句话说HTTP协议本身只保证协议报文的格式符合HTTP的要求,除此之外的传输和网络通信其实都是需要依赖更下层的协议完成。
HTTP设计成如此简单的形式,本质上就是除开协议本身外的内容一切都不考虑,达到高速传输的效果。但是因为HTTP的简单粗暴,协议本身需要很多辅助的组件来完成WEB的各种访问效果,比如保持登陆状态,保存近期的浏览器访问信息,记忆密码等功能,这些都需要Cookie以及Session来完成。
HTTP/1.1引入了Cookie以及Session协助HTTP完成状态存储等操作。
请求资源定位
HTTP大多数时候是通过URL的域名来访问资源的,定位URL要访问的真实服务需要DNS的配合,DNS是什么这里不再赘述。
如果是对服务器访问请求,可以通过
*
的方式发起请求,比如OPTIONS * HTTP/1.1
。
请求方法
实际上用的比较多的还是GET
和POST
。
GET
:通常视为无需要服务端校验可以直接通过URL公开访问的资源,但是通常会在URL中携带大量的请求参数,但是这些参数通常无关敏感信息所以放在URL当中非常方便简单。
POST
:通常情况为表单提交的参数,需要服务端的拦截校验才能获取,比如下载文件或者访问一些敏感资源,实际上POST请求要比GET请求使用更为频繁,因为POST请求对于请求的数据进行“加密”保护。相比于GET请求要安全和靠谱很多。
持久连接
所谓的持久连接包含一定的历史原因,HTTP1.0最早期每次访问和响应都是一些非常小的资源交互,所以一次请求结束之后基本就可以和服务端断开,等到下一次需要再次请求再次连接。
但是随着互联网发展,资源包越来越大,对于互联网的需求和挑战也越来越大。
在后续 HTTP/1.1 中所有的连接默认都是持久连接,目的是减少客户端和服务端的频繁请求连接和响应。
管道化
注意HTTP真正意义上的全双工的协议是在HTTP/2才实现的,实现的核心是多路复用。
管道化可以看做是为了让半双工的HTTP1.1也能支持全双工协议的一种强化,通俗的话说就是围魏救赵。
全双工协议:指的是HTTP连接的两端不需要等待响应数据给对方就可以直接发送请求给对方,实现同一时间内同时处理多个请求和响应的功能。
HTTP/1.1允许多个http请求通过一个套接字同时被输出 ,而不用等待相应的响应(这里提示一下管道化同样需要连接双方都支持才能完成)。
需要注意这里本质上是在一个TCP请求封装了多次请求然后直接丢给服务端去处理,客户端接下来可以干别的事情,要么等待服务端慢慢等待,要么自己去访问别的资源。
客户端通过FIFO队列把多个TCP请求封装成一个发给了服务端,服务端虽然可以通过处理FIFO队列的多个请求,但是必须等所有请求完成再按照FIFO发送的顺序挨个响应回去,也就是说其实并没有根本上解决堵塞问题。
管道化的技术虽然很方便,但是限制和规矩比好处要多得多,并且有点脱裤子放屁的意思。结果是并没有十分普及也没有多少服务端使用,多数的HTTP请求也会禁用管道化防止服务端请求堵塞迟迟不进行响应。
管道化小结
- 实际上管道化可以看做原本阻塞在客户端一条条处理的请求,变为阻塞在服务端的一条条请求。
- 管道化请求通常是GET和HEAD请求,POST和PUT不需要管道化, 管道化只能利用已存在的
keep-alive
连接。 - 管道化是HTTP1.1协议下,服务器不能很好处理并行请求的改进,但是这个方案不理想,围魏救赵失败并且最终被各大浏览器禁用掉。
- FIFO队列的有序和TCP 的有序性区别可以简单认为是强一致性和弱一致性的区别。FIFO队列有序性指的是请求和响应必须按照队列发送的规则完全一样,而TCP仅仅是保证了发送和响应的大致逻辑顺序,真实的情况和描述的情况可能不一致。
- 因为管道是把累赘丢给了服务端,从客户端的角度来看自己完成了全双工的通信。实际上这只是伪全双工通信。
Cookie
Cookie的内容不是本书重点,如果需要了解相关知识可以直接往上查询资料了解,基本一抓一大把。
@H_313_1404@2.2.5 [HTTP/2](HTTP/2 (ietf.org))HTTP2的协议改动比较大,从整体上来看主要是下面一些重要调整:
- SPDY:这个概念是谷歌提出的,起初是希望作为一个独立协议,但是最终SPDY的相关技术人员参与到了HTTP/2,所以谷歌浏览器后面全面支持HTTP/2放弃了SPDY单独成为协议的想法,对于SPDY,具有如下的改进点:
- HTTP Speed + mobility:微软提出改善移动端通信的速度和性能标准,它建立在 Google 公司提出的 SPDY与 WebSocket 的基础之上。
- Network-Friendly HTTP Upgrade:移动端通信时改善 HTTP 性能。
从三者的影响力来看,显然是Google的影响力是最大的,从HTTP3.0开始以谷歌发起可以看出HTTP协议的标准制定现在基本就是谷歌说了算。
接着我们就来看看最重要SPDY,谷歌是一个极客公司,SPDY可以看做是HTTP1.1和HTTP/2正式发布之间谷歌弄出来的一个提高HTTP协议传输效率的“玩具” ,重点优化了HTTP1.X的请求延迟问题,以及解决HTTP1.X的安全性问题:
- 降低延迟(多路复用):使用多路复用来降低高延迟的问题,多路复用指的是使用Stream让多个请求可以共享一个TCP连接,解决HOL Blocking(head of line blocking)(队头阻塞)的问题,同时提升带宽利用率。
- 服务端推送:HTTP1.X的推送都是半双工,所以在2.0是实现真正的服务端发起请求的全双工,另外在WebSocket在这全双工一块大放异彩。
- 请求优先级:针对引入多路复用的一个兜底方案,多路复用使用多个Stream的时候容易单请求阻塞问题。也就是前文所说的和管道连接一样的问题,SPDY通过设置优先级的方式让重要请求优先处理,比如页面的内容应该先进行展示,之后再加载CSS文件美化以及加载脚本互动等等,实际减少用户不会在等待过程中关闭页面的几率。
- Header压缩:HTTP1.X的header很多时候都是多余的,所以2.0 会自动选择合适的压缩算法自动压缩请求加快请求和响应速度。
- 基于HTTPS的加密协议传输:HTTP1.X 本身是不会加入SSL加密的,而2.0 让HTTP自带SSL,从而提高传输可靠和稳定性。
这些内容在后续大部分都被HTTP/2 采纳,下面就来看看HTTP/2具体的实施细节。
HTTP/2具体实施(重点)
当然这一部分也只讲到了协议中一些重点的升级内容,详细内容请参考“参考资料”或者点击HTTP/2的标题。
二进制帧(Stream)
HTTP/2 使用流(二进制)替代 ASCII 编码传输提升传输效率,客户端发送的请求都会封装为带有编号的二进制帧,然后再发送给服务端处理。
HTTP/2 通过 一个TCP连接完成多次请求操作,服务端接受流数据并且检查编号将其合并为一个完整的请求内容,这样同样需要按照二进制帧的拆分规则拆分响应。像这样利用二进制分帧 的方式切分数据,客户端和服务端只需要一个请求就可以完成通信,也就是SPDY提到的多个Stream 合并到一个TCP连接中完成。
二进制分帧把数据切分成更小的消息和帧,采用了二进制的格式进行编码,在HTTP1.1
当中首部消息封装到Headers当中,然后把Request body
封装到 Data帧。
HTTP2.0
使用二进制分帧目的是向前兼容,需要在应用层和传输层之间加一层二进制分帧层,让HTTP1.X 协议更加简单的升级同时不会对过去的协议产生冲突 。
帧、消息、Stream之间的关系
- 帧:可以认为是流当中的最小单位。
- 消息:表示HTTP1.X中的一次请求。
- Stream:包含1条或者多条message。
二进制分帧结构
二进制分帧结构主要包含了头部帧和数据帧两个部分,头部在帧数只有9个字节,注意R属于标志位保留。所以整个算下来是:
3个字节帧长度+1个字节帧类型 + 31bit流标识符、1bit未使用标志位 构成。
image-20220801174510263
帧长度:数据帧长度,24位的3字节大小,取值为 2^14(16384) - 2^24(1677215)之间,接收方的 SETTINGS_MAS_FRAM_SIZE 设置。
帧类型:分辨数据帧还是控制帧。
标志位:携带简单控制信息,标志位表示流的优先级。
流标识符:表示帧属于哪一个流的,上限为2的31次方,接收方需要根据流标识的ID组装还原报文,同一个Stream的消息必须是有序的。此外客户端和服务端分别用奇数和偶数标识流,并发流使用了标识才可以应用多路复用。
R:1位保留标志位,暂未定义,0x0为结尾。
帧数据:实际传输内容由帧类型指定。
如果想要知道更多细节,可以参考“参考资料”部分的官方介绍以及结合WireShark抓包使用,本读书笔记没法面面俱到和深入
最后是补充帧类型的具体内容,帧类型定义了10种类型的帧数:
image-20220801174445090
多路复用 (Multiplexing)
有了前面二进制帧结构的铺垫,现在再来看看多路复用是怎么回事,这里首先需要说明在过去的HTTP1.1中存在的问题:
同一时间同一域名的请求存在访问限制,超过限制的请求会自动阻塞。
在传统的解决方案中是利用多域名访问以及服务器分发的方式让资源到特定服务器加载,让整个页面的响应速度提升。比如利用多个域名的CDN进行访问加速
随着HTTP/2的更新,HTTP2改用了二进制帧作为替代方案,允许单一的HTTP2请求复用多个请求和响应内容,也就是说可以一个包里面打包很多份“外卖”一起给你送过来。
image-20220801174432454
此外,流控制数据也意味着可以支持多流并行而不过多依赖TCP,因为通信缩小为一个个帧,帧内部对应了一个个消息,可以实现并行的交换消息。
Header压缩(Header Compression)
HTTP1.X 不支持Header压缩,如果页面非常多的去看下会导致带宽消耗和不必要的浪费。
针对这个问题在SPDY中 的解决方案是利用DEFLATE 格式的字段,这种设计非常有效,但是实际上存在CRIME信息泄露的攻击手段。
在HTTP/2 当中定义了HPACK,HPACK算法通过静态的哈夫曼编码对于请求头部进行编码减少传输大小,但是需要让客户端和服务端之间维护首部表,首部表可以维护和存储之前发过的键值对信息,对于重复发送的报文内容可以直接通过查表获取,减少冗余数据产生,后续的第二个请求将会发送不重复的数据。
HPACK压缩算法主要包含两个模块,索引表和哈夫曼编码,索引表同时分为动态表和静态表,静态表内部预定义了61个Header的K/V 数值,而动态表是先进先出的队列,初始情况下内容为空,而解压header则需要每次添加的时候放到队头,移除从队尾开始。
注意动态表为了防止过度膨胀占用内存导致客户端崩溃,在超过一定长度过后会自动释放HTTP/2请求。
HPACK算法
HPACK算法是对于哈夫曼算法的一种应用和改进,哈夫曼算法经典案例是就是ZIP压缩,也就是虽然我们可能不清楚却是可能天天在用的一个东西。
HPACK算法的思路是在客户端和服务端两边各维护一个哈希表,然后双端通过表中缓存Headers字段减少流中二进制数据传输,进而提高传输效率。
HPACK三个主要组件有如下细节:
- 静态表:HTTP2为出现在头部的字符串和字段静态表,包含61个基本的headers内容,
- 动态表:静态表只有61个字段,所以利用动态表存储不在静态表的字段,从62开始进行索引,在传输没有出现的字段时候,首先对于建立索引号,然后字符串需要经过哈夫曼编码完成二进制转化发给服务器,如果是第二次发送则找到对应的动态表的索引找到即可,这样有效避免一些冗余数据的传输。
- 哈夫曼编码:这一算法非常重要,对于近代互联网的发展有着重大影响。
哈夫曼编码:是一种用于无损数据压缩的熵编码(权编码)算法。由美国计算机科学家大卫·霍夫曼(David Albert Huffman)在1952年发明。霍夫曼在1952年提出了最优二叉树的构造方法,也就是构造最优二元前缀编码的方法,所以最优二叉树也别叫做霍夫曼树,对应最优二元前缀码也叫做霍夫曼编码。
下面为哈夫曼编码对应的原始论文:
哈夫曼编码原始论文: 链接:https://pan.baidu.com/s/1r_yOVytVXb-zlfZ6csUb2A?pwd=694k 提取码:694k
此外这里有个讲的比较通俗的霍夫曼的视频,强烈建议反复观看,能帮你快速了解哈夫曼编码是怎么回事,当然前提是得会使用魔法。
https://www.youtube.com/watch?v=Jrje7ep5Ff8&t=29s
请求优先级
请求优先级实际上并不是HTTP/2才出现的,在此之前的的RFC7540中定义了一套优先级的相关指令,但是由于它过于复杂最后并没被普及,但是里面的信息依然是值得参考的。
HTTP/2的内容取消了所有关于RFC7540优先级的指令,所有的描述被删除并且被保留在原本的协议当中。
HTTP/2利用多路复用,所以有必要优先使用重要的资源分配到前面优先加载,但是实际上在实现方案过程中优先级是不均衡的,许多服务器实际上并不会观察客户端的请求和行为。
最后还有根本性的缺点,也就是TCP层是无法并行的,在单个请求当中的使用优先级甚至有可能性能弱于HTTP1.X。
流量控制
所谓流量控制就是数据流之间的竞争问题,需要注意HTTP2只有流数据才会进行控制,通过使用WINDOW_UPDATE
帧来提供流量控制。
注意长度不是4个八位字节的window_update
帧需要视为 frame_size_error
的错误进行响应。
PS:下面的设计中有效载荷是保留位+ 31位的无符号整数,表示除了现在已经有的流控制窗口之外还能额外传输8个字节数的数据,所以最终合法范围是
1到 2^31 - 1 (2,147,483,647)
个八位字节。
WINDOW_UPDATE Frame {
Length (24) = 0x04,
Type (8) = 0x08,
Unused Flags (8),
Reserved (1),
Stream Identifier (31),
Reserved (1),
Window Size Increment (31),
}
对于流量控制,存在下面几个显著特征:
- 流量控制需要基于HTTP中间的各种代理服务器控制,不是非端到端的控制;
- 基于信用基础公布每个流在每个连接上接收了多少字节,WINDOW_UPDATE 框架没有定义任何标志,并没有强制规定;
- 流量的控制存在方向概念,接收方负责流量控制,并且可以设置每一个流窗口的大小;
-
WINDOW_UPDATE
可以对于已设置了END_STREAM
标志的帧进行发送,表示接收方这时候有可能进入了半关闭或者已经关闭的状态接收到WINDOW_UPDATE帧,但是接收者不能视作错误对待; - 接收者必须将接收到流控制窗口增量为 0 的
WINDOW_UPDATE
帧视为PROTOCOL_ERROR类型的流错误 ;
服务器推送
服务器推送意图解决HTTP1.X中请求总是从客户端发起的弊端,服务端推送的目的是更少客户端的等待以及延迟。但是实际上服务端推送很难应用,因为这意味着要预测用户的行为。服务端推送包含推送请求和推送响应的部分。
推送请求
推送请求使用PUSH_PROMISE
帧作为发送,这个帧包含字段块,控制信息和完整的请求头字段,但是不能携带包含消息内容的相关信息,因为是指定的帧结构,所以客户端也需要显式的和服务端进行关联,所以服务端推送 请求也叫做“Promised requests”。
当请求客户端接收之后是传送CONTINUATION
帧,CONTINUATION
帧头字段必须是一组有效的请求头字段,服务器必须通过":method"
伪字段头部添加安全可缓存的方法,如果客户端收到的缓存方法不安全则需要在PUSH_PROMISE
帧上响应错误,这样的设计有点类似两个特务对暗号,一个暗号对错了就得立马把对方弊了。
PUSH_PROMISE
可以在任意的客户端和服务端进行传输,但是有个前提是流对于服务器需要保证“半关闭“或者“打开“的状态,否则不允许通过CONTINUATION
或者HEADERS
字段块传输。
PUSH_PROMISE
帧只能通过服务端发起,因为专为服务端推送设计,使用客户端推送是“不合法“的。
PUSH_PROMISE
帧结构:
再次强调有效载荷是一个保留位+ 31位的无符号整数。有效载荷是什么?是对于HTTP1.1协议中实体的术语重新定义,可以简单看做是报文的请求Body。
下面是对应源代码定义:
PUSH_PROMISE
帧定义
PUSH_PROMISE Frame {
Length (24),
Type (8) = 0x05,
Unused Flags (4),
PADDED Flag (1),
END_HEADERS Flag (1),
Unused Flags (2),
Reserved (1),
Stream Identifier (31),
[Pad Length (8)],
Reserved (1),
Promised Stream ID (31),
Field Block Fragment (..),
Padding (..2040),
}
CONTINUATION
帧:用于请求接通之后继续传输,注意这个帧不是专用于服务端推送的。
CONTINUATION Frame {
Length (24),
Type (8) = 0x09,
Unused Flags (5),
END_HEADERS Flag (1),
Unused Flags (2),
Reserved (1),
Stream Identifier (31),
Field Block Fragment (..),
}
推送响应
如果客户端不想接受请求或者服务器发起请求的时间过长,可以通过RST_STREAM
帧代码标识发送CANCEL
或者REFUSED_STREAM
内容告诉服务器自己不接受服务端请求推送。
而如果客户端需要接收这些响应信息,则需要按照之前所说传递CONTINUATION
以及PUSH_PROMISE
接收服务端请求。
其他特点:
- 客户端可以使用SETTINGS_MAX_CONCURRENT_STREAMS设置来限制服务器可以同时推送的响应数量。
- 如果客户端不想要接收服务端的推送流,可以把SETTINGS_MAX_CONCURRENT_STREAMS设置为0或者重置
PUSH_PROMISE
保留流进行处理。
2.2.6 HTTP/3
进度追踪:RFC 9114 - HTTP/3 (ietf.org)
为什么会存在3?
可以发现HTTP/2虽然有了质的飞跃,但是因为TCP协议本身的缺陷,队头阻塞的问题依然可能存在,同时一旦出现网络拥堵会比HTTP1.X情况更为严重(用户只能看到一个白板)。
所以后续谷歌的研究方向转为研究QUIC,实际上就是改良UDP协议来解决TCP协议自身存在的问题。但是现在看来这种改良不是很完美,目前国内部分厂商对于QUIC进行自己的改进。
HTTP/3 为什么选择UDP
这就引出另一个问题,为什么3.0有很多协议可以选择,为什么使用UDP?通常有下面的几个点:
- 基于TCP 协议的设备很多,兼容十分困难。
- TCP是Linux内部的重要组成,修改非常麻烦,或者说压根不敢动。
- UDP本身无连接的,没有建立连接和断连的成本。
- UDP数据包本身就不保证稳定传输所以不存在阻塞问题(属于爱要不要)。
- UDP改造相对其他协议改造成本低很多
HTTP/3 新特性
- QUIC(无队头阻塞):优化多路复用,使用QUIC协议代替TCP协议解决队头阻塞问题,QUIC也是基于流设计但是不同的是一个流丢包只会影响这一条流的数据重传,TCP 基于IP和端口进行连接,多变的移动网络环境之下十分麻烦,QUIC通过ID识别连接,只要ID不变,网络环境变化是可以迅速继续连接的。
- 0RTT:注意建立连接的0TT在HTTP/3上目前依然没有实现,至少需要1RTT。
RTT:RTT是Round Trip Time的缩写,简单来说就是通信一来一回的时间。RTT包含三部分:
- 往返传播延迟。
- 网络设备排队延迟。
- 应用程序处理延迟。
HTTPS建立完整连接通常需要TCP握手和TLS握手,至少要2-3个RTT,普通的HTTP也至少要1个RTT。QUIC的目的是除开初次连接需要消耗1RTT时间之外,其他的连接可以实现0RTT。
为什么无法做到初次交互0RTT?因为初次传输说白了依然需要传输两边到密钥信息,因为存在数据传输所以依然需要1个RTT的时间完成动作,但是在完成握手之后的数据传输只需要0RTT的时间。
- 前向纠错:QUIC的数据包除了本身的内容之外,还允许携带其他数据包,在丢失一个包的时候,通过携带其他包的数据获取到丢包内容。 具体要怎么做呢?例如3个包丢失一个包,可以通过其他数据包(实际上是校验包)异或值计算出丢失包的“编号”然后进行重传,但是这种异或操作只能针对一个数据包丢失计算,如果多个包丢失,用异或值是无法算出一个以上的包的,所以这时候还是需要重传(但是QUIC重传代价比TCP的重传低很多)。
- 连接迁移:QUIC放弃了TCP的五元组概念,使用了64位的随机数ID充当连接ID,QUIC 协议在切换网络环境的时候只要ID一致就可以立马重连。对于现代社会经常wifi和手机流量切换的情况十分好用的一次改进。
术语解释⚠️: 5元组:是一个通信术语,英文名称为five-tuple,或5-tuple,通常指由**源Ip (source IP), 源端口(source port),目标Ip (destination IP), 目标端口(destination port),4层通信协议 (the layer 4 protocol)**等5个字段来表示一个会话,是会话哦。这个概念在《网络是怎么样连接的》这本书中也有提到类似的概念。那就是在第一章中创建套接字的步骤,创建套接字实际上就需要用到这个五元祖的概念,因为要创建“通道”需要双方给自告知自己的信息给对方自己的IP和端口,这样才能完成通道创建和后续的协议通信。 顺带拓展一下4元组和7元组。 4元组:即用4个维度来确定唯一连接,这4个维度分别是**源Ip (source IP), 源端口(source port),目标Ip (destination IP), 目标端口(destination port)**。 7元组:即用7个字段来确定网络流量,即源Ip (source IP), 源端口(source port),目标Ip (destination IP), 目标端口(destination port),4层通信协议 (the layer 4 protocol),服务类型(ToS byte),接口索引(Input logical interface (ifIndex))
- 加密认证的报文:
QUIC
默认会对于报文头部加密,因为TCP头部公开传输,这项改进非常重要。 - 流量控制,传输可靠性:
QUIC
在UDP
协议上加了一层数据可靠传输的可靠性传输,因此流量控制和传输可靠性都可以得到保证。 -
帧格式变化
下面是网上资料对比HTTP2和3之间的格式差距,可以发现
HTTP/3
帧头只有两个字段:类型和长度。帧类型用来区分数据帧和控制帧,这一点是继承自HTTP/2的变化,数据帧包含HEADERS帧,DATA帧,Http包体。
帧格式变化
- 关于2.0的头部压缩算法升级成了
QPACK
算法:需要注意HTTP3的 **QPACK
**算法与HTTP/2
中的HPACK
编码方式相似,HTTP/3
中的QPACK
也采用了静态表、动态表及Huffman
编码。 那么相对于之前的算法HPACK
,QPACK
算法有什么升级呢?首先HTTP/2
中的HPACK
的静态表只有 61 项,而HTTP/3
中的QPACK
的静态表扩大到 91 项。 最大的区别是对于动态表做了优化,因为在HTTP2.0中动态表存在时序性的问题。 所谓时序性问题是在传输的时候如果出现丢包,此时一端的动态表做了改动,但是另一端是没改变的,同样需要把编码重传,这也意味着整个请求都会阻塞掉。
时序性问题
因此HTTP3使用UDP的高速,同时保持QUIC的稳定性,并且没有忘记TLS的安全性,在2018年的YTB直播中宣布QUIC作为HTTP3的标准。
YTB 地址:(2) IETF103-HTTPBIS-20181108-0900 - YouTube,可怜互联网的天花板协议制定团队IETF连1万粉丝都没有。
HTTP3协议改进
2.3 HTTP部分问题讨论
2.3.1 队头阻塞问题(head of line blocking)
队头阻塞问题不仅仅只是处在HTTP的问题,实际上更加底层的协议以及网络设备通信也会存在线头阻塞问题。
交换机
当交换机使用FIFO队列作为缓冲端口的缓冲区的时候,按照先进先出的原则,每次都只能是最旧的网络包被发送,这时候如果交换机输出端口存在阻塞,则会发生网络包等待进而造成网络延迟问题。
但是哪怕没有队头阻塞,FIFO队列缓冲区本身也会卡住新的网络包,在旧的网络包后面排队发送,所以这是FIFO队列本身带来的问题。
有点类似核酸排队,前面的人不做完后面的人做不了,但是前面的人一直不做,后面也只能等着。
交换机HO问题解决方案
使用虚拟输出队列的解决方案,这种方案的思路是只有在输入缓冲区的网络包才会出现HOL阻塞,带宽足够的时候不需要经过缓冲区直接输出,这样就避免HOL阻塞问题。
无输入缓冲的架构在中小型的交换机比较常见。
线头阻塞问题演示
交换机:_交换机根据 MAC 地址表查找 MAC 地址, 然后将信号发送到相应的端口_一个网络信号转接设备,有点类似电话局中转站。
线头阻塞示例:第 1 和第 3 个输入流竞争时,将数据包发送到同一输出接口,在这种情况下如果交换结构决定从第 3 个输入流传输数据包,则无法在同一时隙中处理第 1 个输入流。
请注意,第一个输入流阻塞了输出接口 3 的数据包,该数据包可用于处理。
无序传输:
因为TCP不保证网络包的传输顺序,所以可能会导致乱序传输,HOL阻塞会显著的增加数据包重新排序问题。
同样为了保证有损网络可靠消息传输,原子广播算法虽然解决这个问题,但是本身也会产生HOL阻塞问题,同样是由于无序传输带来的通病。
Bimodal Multicast 算法是一种使用 gossip 协议的随机算法,通过允许乱序接收某些消息来避免线头阻塞。
HTTP线头阻塞
HTTP 在 2.0 通过多路复用的方式解决了HTTP协议的弱点并且真正意义上消除应用层HOL阻塞问题,但是TCP协议层的无序传输依然是无法解决的。
于是在3.0中直接更换TCP协议为 QUIC 协议消除传输层的HOL阻塞问题。
2.4.2 HTTP/2 全双工支持
注意HTTP直到2.0才是真正意义上的全双工,所谓的HTTP支持全双工是混淆了TCP协议来讲的,因为TCP是支持全双工的,TCP可以利用网卡同时收发数据。
为了搞清楚TCP和HTTP全双工的概念, 应该理解HTTP中双工的两种模式:半双工(http 1.0/1.1),全双工(http 2.0)。
半双工:同一时间内链接上只能有一方发送数据而另一方接受数据。
- http 1.0 是短连接模式,每个请求都要建立新的 tcp 连接,一次请求响应之后直接断开,下一个请求重复此步骤。
- http 1.1 是长连接模式,可以多路复用,建立 tcp 连接不会立刻断开,资源1 发送响应,资源2 发送响应,资源3 发送响应,免去了要为每个资源都建立一次 tcp 的开销。
全双工:同一时间内两端都可以发送或接受数据 。
- http 2.0 资源1客户端发送请求不必等待响应就可以继续发送资源2 的请求,最终实现一边发,一边收。
2.4.3 HTTP 2.0 缺点
- 解决了HTTP的队头请求阻塞问题,但是没有解决TC P协议的队头请求阻塞问题,此外HTTP/2需要同时使用TLS握手和 HTTP握手耗时,同时在HTTPS连接建立之上需要使用TLS进行传输。
- HTTP/2的队头阻塞出现在当TCP出现丢包的时候,因为所有的请求被放到一个包当中,所以需要重传,TCP 此时会阻塞所有的请求。但是如果是HTTP1.X,那么至少是多个TCP连接效率还要高一些,
- 多路复用会增大服务器压力,因为没有请求数量限制,短时间大量请求会瞬间增大服务器压力
- 多路复用容易超时,因为多路复用无法鉴定带宽以及服务器能否承受多少请求。
丢包不如HTTP1.X
丢包的时候出现的情况是HTT P2.0因为请求帧都在一个TCP连接,意味着所有的请求全部要跟着TCP阻塞,在以前使用多个TCP连接来完成数据交互,其中一个阻塞其他请求依然可以正常抵达反而效率高。
二进制分帧目的
根本目的其实是为了让更加有效的利用TCP底层协议,使用二进制传输进一步减少数据在不同通信层的转化开销。
HTTP1.X的Keep-alive缺点
2.4.4 HTTP协议真的是无状态的么?
仔细阅读HTTP1.x和HTTP/2以及HTTP3.0三个版本的对比,其实会发现HTTP无状态的定义偷偷发生了变化的,为什么这么说?
在讲解具体内容之前,我们需要弄清一个概念,那就是Cookie和Session虽然让HTTP实现了“有状态”,但是其实这和HTTP协议本身的概念是没有关系的。
Cookie
和Session
的出现根本目的是保证会话状态本身的可见性,两者通过创立多种独立的状态“模拟”用户上一次的访问状态,但是每一次的HTTP请求本身并不会依赖上一次HTTP的请求,单纯从广义的角度看待其实所有的服务都是有状态的,但是这并不会干扰HTTP1.X本身无状态的定义。
此外HTTP协议所谓的无状态指的是每个请求是完全的独立的,在1.0备忘录定义也可以看出一次HTTP连接其实就是一次TCP连接,到了HTTP1.1实现了一个TCP多个HTTP连接依然是可以看做独立的HTTP请求。
说了这么多,其实就是说HTTP1.X在不靠Cookie和Session扶着的时候看做无状态是对的,就好比游戏里面的角色本身的数值和武器附加值的对比,武器虽然可以让角色获得某种状态,但是这种状态并不是角色本身特有的,而是靠外力借来的。
然而随着互联网发展,到了HTTP/2和HTTP3之后,HTTP本身拥有了“状态”定义。比如2.0关于HEADER压缩产生的HPACK算法(需要维护静态表和动态表),3.0还对HPACK算法再次升级为QPACK让传输更加高效。
所以总结就是严谨的来说HTTP1.X是无状态的,在Cookie和Session的辅助下实现了会话访问状态的保留。
到了HTTP/2之后HTTP是有状态的, 因为在通信协议中出现了一些状态表来维护双方重复传递的Header字段减少数据传输。
2.4 小结
这一章节本来应该是全书的核心内容,奈何作者似乎并不想让读者畏惧,所以讲的比较浅显,个人花费了不少精力收集网上资料结合自己的思考整理出第二章的内容。
关于HTTP的整个发展史是有必要掌握的,因为八股有时候会提到相关问题,问的深入一些确实有些顶不住,HTTP 协议也是应用层通信协议的核心,其次作为WEB开发人员个人认为是更是有必要掌握的。
另外了解HTTP的设计本身可以让我们过渡到TCP协议的了解,TCP的设计导致了HTTP设计的影响等问题可以做更多思考。
关于更多内容建议可以看看《网络是怎么样连接》的这一篇读书笔记,原书从整个TCP/IP 结构的角度通俗的讲述了有关互联网发展的基本脉络,而这一篇讲述了HTTP发展的基本历史和未来的发展方向。
三、《图解HTTP》- 报文内的 HTTP信息
知识点
3.1 HTTP请求报文结构
请求和响应报文的结构如下:
HTTP请求报文结构
下面是有关请求报文请求和响应的案例。
请求报文请求和响应的案例
3.2 报文和主体差异
为了提高HTTP传输效率,在请求中可以通过HTTP请求报文和实体加工的方式对于报文原文进行“编码”,这里的编码并不是单指文本字符串,而是更抽象意义上的编码。
介绍具体的内容之前我们需要先分清楚两个术语:报文和实体。
报文:是HTTP通信中的基本单位,由8位组 字节流(octet sequence,其中octet为8个比特)组成,通过HTTP通信传输。
实体(entity):作为请求或响应的 有效载荷数据(补充项)被传输,其内容由实体首部和实体主体组成。
为了理解实体的概念,需要了解有效载荷是怎么一回事:
负载(英语:Payload):负载指的是需要传输的实体数据信息,这也是为什么叫数据实体的原因。当然也可以叫做信头与元数据,或称为开销数据,仅用于辅助数据传输。
头(header):指的是在一块数据存储或传输之际在头追加的数据,这些信息是对数据区的描述。
元数据(英语:Metadata):……为描述其他数据信息的数据。
划掉实体是因为术语实体(entity)被有效载荷(payload)代替,书中所提到2616版本很多解释已经被废弃了,现在**RFC 2616 **已经被 RFC 7230 、 7235 取 代了。 下面这篇文章中讨论了实体和载荷的区别,以及为什么要取代载荷 #109 (Clarify entity / representation / variant terminology) – Hypertext Transfer Protocol Wiki (ietf.org)
有关负载的解释
原文:
Replaced entity with payload and variant with representation. Cleaned up description of 204 status code (related to ticket #22) Rewrote section on Content-Location and refer to def in RFC2557.
另外维基上有一个关于生活当中“有效载荷”的术语解释,通过描述可以从侧面理解官方为什么突然要把实体的概念重新解释。
摘自维基百科“有效载荷”:有效载荷是飞机或运载火箭携带的物体。有时,有效载荷也指飞机或运载火箭能够承载的重量。根据任务的性质,载具的有效载荷可能是货物、乘客、机组人员、弹药、科学仪器或实验或其他设备。如果可以选择性携带,那额外的燃料也会被视为有效载荷的一部分,如空中加油任务。
个人认为负载(叫负荷也可以)这个解释要比实体这个解释好理解一些(实体稍显抽象),并且不会丢失实体本身的含义。
接着我们通过对比Chrome和Edge浏览器,发现在目前的版本中均存在负载的概念,过去的版本中实际上这部分内容被放到报文的请求实体 中,很显然这是不严谨的,在那个时候被称作实体。
当然这两年这部分悄悄做了调整,显然在后续RFC修订协议过程中这些浏览器也对于这些概念进行跟进,不知道有多少人关注过,嗯,又是一个小细节。
Edge的“负载”
Chrome的Payload
所以负载概念取代实体概念目的是防止混淆(因为确实很容易搞混),实际上实体也分为首部和其他信息,实体首部是对该负载的描述,而负载和其它一些信息(请求行/状态行、各种首部字段等等)组织成报文进行传输。
书中有这样的图帮助我们了解实体和报文的差别,这张图也能说明为什么很多解释会把报文和实体(有效负载)看做是订单和货物的关系。
请求和响应报文结构
更头疼的概念
实际上还用更容易混淆的概念,**message body
** 和 **payload body
**。
根据 RFC 7230:
HTTP 报文的报文主体(message body)(如果存在的话)是用来运载请求或响应的有效载荷主体(payload body)的。除非应用了传输编码,报文主体等价于有效载荷主体。
换句话说只有在应用了传输编码的时候,负载=实体首部+实体主体,目前主要应用的传输编码是transfer-encoding: chunked
,也就是分块传输的去看下负载的概念会出现转变,否则可以简单看做是报文的请求Body。
HTTP报文的主体用于传输请求或响应的实体主体,对于主体的处理优化HTTP在后续的版本中实现了下面这些特性:
- 压缩传输
- 分块传输编码
- 多数据多对象集合
压缩传输
首先需要明确到的是压缩是在负载上面完成的,并且压缩需要保证信息不遗失的原样压缩,否则压缩不完整的数据会导致数据发生错误。
常见的压缩方式是下面几种,其中gzip是图片经常使用的压缩方式:
- `gzip(GNU zip)``
compress(UNIX 系统的标准压缩)
deflate(zlib)
identity(不进行编码)
压缩传输是有代价的,因为这个操作需要计算机完成,所以会增加服务器的工作量,不过这一点开销完全可以接受。
分块传输编码
实体主体分块的功能称为分块传输编码(Chunked TransferCoding),分块传输指的是传输编码会将实体内容拆分为多个块(chunck),也就是前文提到的transfer-encoding: chunked
。
需要注意在负载主体的最后一块会使用“0(CR+LF)”来标记块的大小。
多数据多对象集合
多数据多对象集主要包含如下内容:
-
mulitpart/form-data
:在 Web 表单文件上传时使用; -
mulitpart/byteranges
:状态码 206(Partial Content,部分内容)响应报文包含了多个范围的内容时使用;
需要使用多数据多对象集合,需要在HTTP中指定Content-Type
首部字段。
enctype 属性
多数据多对象集合的一个代表属性,主要的作用是告知服务器自己将会传输什么类型的数据。
最常见的多部分对象集合的实际应用就是使用 HTML 表单发送文件。文件是二进制数据(或被视为二进制数据),而所有其他数据都是文本数据。由于 HTTP 是一种文本协议,因此对处理二进制数据有特殊要求。
3.3 内容协商
内容协商比较典型的案例是国际化,内容协商有点类似转译,服务器和客户端之间需要协商出一种最为合适的“中间”语言进行交流,然后按照字符集和编码格式进行交互。
基准和判断的基准是下面这几个首部字段的信息:
Accept
Accept-Charset
Accept-Encoding
Accept-Language
Content-Language
比如下面的维基在请求请求首部中就用到了这些信息。
content-encoding: gzip
content-language: zh
content-length: 17396
accept-ch: Sec-CH-UA-Arch,Sec-CH-UA-Bitness,Sec-CH-UA-Full-Version-List,Sec-CH-UA-Model,Sec-CH-UA-Platform-Version
3.3.1 内容协商方式
内容协商的基本准则如下:
服务器驱动协商(Server-driven Negotiation)
由服务器端进行内容协商。服务端协商中客户端请求随同URL会发送一份消息头表明自己的倾向性,服务器按照这个倾向性选择合适的资源返回。
服务器驱动的优点是充分利用HTTP协议规范减少额外的行为,因为是内容协商而不是格式协商,决定权实际上还是在服务端这一边。
当然这样的优点导致的代价是服务端的复杂性增加,因为需要“猜测”客户端的信息,同时可能会导致客户端发送报文越来越复杂。
客户端驱动协商(Agent-driven Negotiation)
由客户端进行内容协商的方式,用户协商类似用户选择浏览器的类型自动进行切换。
注意客户端驱动如果服务端不能回应客户端的请求,会退化为 服务器驱动协商,客户端驱动为了获取自己想要的内容需要 第二次发送请求(第一次获取列表,第二次才是得到资源),可见客户端的驱动模式并不是一种常用的方式。
代理驱动型内容协商机制
针对透明代理的改良方案,代理驱动主要是解决服务端协商的比较显著的痛点:规模化问题。
所谓的规模化问题指的当服务端请求出现大量资源并且需要添加首部情况下,会出现请求体积膨胀并且精确信息的发送也带来信息泄露问题。
注意代理驱动和透明代理存在一定区别,它使用了HTTP协议自创建依赖就支持又称为响应代理机制的东西,这种机制也是和客户端驱动协商类似,返回资源列表给用户进行选择然后需要第二次请求获取需要的资源。而透明代理借用了 vary首部完成协议兼容,有点类似“旁外招”。
所以代理驱动虽然减轻了服务端和客户端形成“中间商”参考的模式,但是也避免不了第二次请求的问题。
透明协商(Transparent Negotiation)
透明代理被代理驱动型内容协商机制取代。
透明协商机制试图从服务器上去除服务器驱动协商所需的负载,并用中间代理来代表客户端以使与客户端的报文交换最小化。
这是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。但是因为后续历史没被认可所以被遗弃。
透明协商在HTTP并没有提供相应的规范,所以HTTP/1.1规范中没有定义任何透明协商机制,但定义了vary
首部,所以透明代理主要使用了vary
这个额外的字段完成协议兼容。
vary 响应首部是什么?在HTTP1.1协议中被添加,是通过服务器响应给客户端协商内容的时候一并返回的,服务端最终使用了那个首部清单。最大的受益者不是客户端反倒是缓存服务器,缓存服务器检查发现vary字段之后启用透明协商机制委托传输。 缓存服务器是啥?请看这篇文章[[《网络是怎么样连接的》读书笔记 - 汇总篇]]中关于负载均衡概念的介绍。
Alternates 首部
同样没受到认可被遗弃。网上都搜不到啥资料,忽略即可。
3.4 小结
上面介绍了众多的 内容协商方式,实际上仔细观察现在的网站会发现服务器驱动协商和代理驱动型内容协商机制为主。
前者是WEB服务提供商可以根据用户的请求推送喜欢的内容,并且不需要二次发送请求节省带宽,适合绝大多数WEB用户,当然用户体验取决于服务端应用程序开发者的水平。
代理驱动型内容协商机制则多用于支持国际化的网站,比如一些大商城或者百科等,比较典型的比如Apple和维基百科等这些网站,提供了“建议”选项询问用户选择哪种语言进行浏览。
而客户端代理主动权掌握在用户手上,服务端无法把控的同时不利于商业推广,所以大部分WEB网站会“屏蔽”这种方式,另一方面代理驱动能减轻服务器压力同时兼容了客户端驱动的特点,所以被代理驱动取代也十分正常。
最后是透明代理,透明代理使用的“旁门左路”的自定义的协议不怎么通用的,所以被淘汰以及被代理驱动取代也很正常。
#tjhttp 八、《图解HTTP》 - HTTPS
知识点
- HTTPS 是什么?HTTP有哪些缺点?
- SSL、TLS为啥总是被放到一起,有什么区别?
- SSL、TLS历史背景。
- SSL的加密细节,加密算法了解。
- SSL的加密流程。
HTTP缺点
- 明文通信,内容容易被窃听。
- 无身份验证,容易受到伪装请求攻击。
- 无法验证报文完整,无法防篡改。
除了协议本身的漏洞之外,一些编程语言也可能编写出不安全的网络应用程序。
明文窃听
既然HTTP是不加密通信的,那么自然会好奇它是如何被窃听的。
所谓的窃听是因为TCP/IP模型的物理层、数据链路层、网络层这几层所需要的设备支持都不可能是个人用户所具备的东西,所以在这几个环节进行通信窃听是完全有可能的。
整个窃听的过程如下图,在网络信息通过网卡发出去的那一刻,网络包中间被“加工”的可能性就会急剧增加。这样的情况就好比一个快递从站点发出去的一刻,就有可能出现各种各样的情况。
此外加密通信并不是保证信息不被窃听,而是在窃听方拿到网络包之后无法破解明文信息内容,这样“加密”的特性就算是达到了。
明文窃听
常见的窃听方式比如WireShark,可以对于请求进行抓包处理。
如何防止窃听
防止明文窃听通过加密进行保护处理的方式有两种:
无身份验证
无身份验证体现在下面几个方面:
- 人人都可以发送请求
- 无法确认响应的服务器是否真实。
- 无法确认发送请求的客户端是否真实。
- 无法验证发送方是否符合权限。
- 无法判定请求来源。
- 无法阻止无意义攻击(Ddos攻击)。
进行身份验证
SSL/TLS 需要通过第三方符合资质的机构进行数字认证,能获得这一机构认证本身就是十分麻烦的事情,所以通常颁发认证证书的服务器基本都可以获得加密保障,同时确认请求方的证书能有效的控制请求来源,对于客户端也能清楚请求的对方是合法安全的。
无法验证报文完整
请求在传输和响应的过程中遭到拦截并且篡改攻击的手段叫做中间人攻击(Man-in-the-Middle attack,MITM),在许多情况下这是很简单的(例如,在一个未加密的Wi-Fi[1] 无线接入点[2]的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)。
一个中间人攻击能成功的前提条件是攻击者能将自己伪装成每一个参与会话的终端,并且不被其他终端识破。中间人攻击是一个(缺乏)相互认证的攻击。
如何防止篡改
针对中间人攻击,HTTP通常使用 MD5
和 SHA-1
等散列值校验的方法,以及用来确认文件的数字签名方法提高安全性。此外Web 网站也会提供相应的以 PGP(Pretty Good Privacy,完美隐私) 创建的数字签名及 MD5 算法生成的散列值。
但是这些手段依然无法完全保证PGP不会被篡改,HTTP本身的可靠保证过于缺乏 。
SSL协议可以验证参与通讯的一方或双方使用的证书,校验是否是由权威的受信任的数字证书认证机构颁发,并且能执行双向身份认证。
以上内容便是HTTP本身暴露的许多缺陷导致的信息泄漏问题,也是为什么要引入SSL/TLS 协议来强化HTTP的协议的几个理由。下面我们来聊聊SSL/TLS的历史。
SSL/TLS 历史
现在我们讨论的SSL实际上是TLS,因为SSL协议本身的各种问题早已经被废弃了,目前主流的SSL实际上使用的是TLS的协议规范。
但是因为SSL被广为流传,结合历史原因,所以依然沿用这样的说法并不会产生歧义。
接着我们得明白HTTP+ 加密 + 认证 + 完整性保护=HTTPS
这个HTTPS的含义。
SSL/TLS 也是类似Cookie和Session一样,在不干扰协议本身运作的情况下对于HTTP协议本身进行保护和增强。
使用HTTPS请求之后,在浏览器输入地址的时候需要将原本的HTTP转化为HTTPS。
无论是OSI 七层模型还是TCP/IP模型,都为每个通信层的职责划分了明确的界限,HTTP是依赖TCP进行数据传输的,但是TCP为了保证单一职责和高效不会搭理HTTP的安全请求(本身也没有),他只负责数据包的收发,所以TCP是不能随便动的。而HTTP同样历史发展悠久,也难以在短时间内对于协议修订和增强。
可以看到,HTTP 是应用层协议,TCP是传输层协议,HTTP依赖TCP完成数据传输,所以 SSL/TLS 肯定是在应用层或者占据着传输层?
传输层必不可能,因为无法HTTP兼容,放到应用层这一说法其实也不完全准确。实际上SSL在TCP和HTTP的中间,类似处在两个应用的夹层里面,也就是所谓的架构难题的绝招 -- 遇事不决加一层。(因为干涉任意一层都引出更多的问题)。
中间夹层不知道为什么让我想到了《黑客帝国3》的那个车站。
SSL协议
明白了SSL/TLS 所处位置,我们继续了解SSL/TLS 的历史。
在许多参考资料中很多时候我们一会儿看到SSL的描述,一会儿看到TLS的描述,首先得再分清两者本身的定义。
如书中所言:
看似是协议和“伪通信层”的东西两个不同的东西,实际上SSL是TLS的前身,或者说TLS出现的本意就是为了替换SSL而出现的“竞品”。
SSL最早出现,出道即拉胯。随着历史发展发现SSL总是存在这样那样的缺点被人诟病。TLS乘胜追击逐渐取代SSL,到了目前最新的版本是TLS1.3(已经有了一半左右的普及度)。
这里参考维基百科的介绍,大致介绍TLS/SSL 的历史。
SSL 1.0、2.0和3.0
- SSL1.0 从来没有发布过,因为存在巨大的安全漏洞和隐患。
- 2.0版本在1995年2月发布后,很快被发现包含许多安全和可用性缺陷。SSL 2.0 在 2011 年被 RFC[3] 6176 弃用。另外SSL 2.0假设只有一个服务和一个固定域证书,这与Web服务器中广泛使用的虚拟主机功能相冲突,因此大多数网站实际上都因使用SSL而受到损害。
弃用原因:- 消息认证使用 MD5 。有安全意识的用户已经不再使用 MD5 [RFC6151]。- 握手消息不受保护。
- 消息完整性和消息加密使用相同的密钥,即如果客户端和服务器协商弱加密,则会出现问题。
- 会话可以轻松终止。中间人可以轻松插入 TCP FIN 关闭会话,对端无法确定这是否是会话的合法结束。
- SSL版本3.0.17 15 1996年发布,它由Paul Kocher与netscape工程师Phil Karlton和Alan Freier合作制作,并由Christopher Allen和Tim Dierks设计的
Consension Development
参考实现。 - 2014年,SSL 3.0被发现容易受到影响SSL中所有块密码的POODLE攻击。RC4 是 SSL 3.0 支持的唯一非块密码,在 SSL 3.0.18 SSL 3.0 中使用时也可能被破解。
- RFC 7568 - Deprecating Secure Sockets Layer Version 3.0 (ietf.org)于 2015 年 6 月弃用SSL3。
所以SSL1.0到3.0都是比较坑的玩意儿,难怪会全面转向TLS,在2018年,谷歌、微软、苹果同时声明废弃TLS1.1、TLS1.0的使用,目前有99% 的服务器支持 TLS 1.2,基本已经完成TLS全面普及。
TLS
TLS 并不是在SSL出现问题之后才出现的,而是在SSL3.0出现之后开始修订。
- TLS 1.0 于 1999 年 1 月在 RFC 2246 - The TLS Protocol Version 1.0 (ietf.org) 中首次定义为 SSL 版本 3.0 的升级。
- TLS 1.1 TLS 1.1 于 2006 年 4 月在 RFC 4346 - The Transport Layer Security (TLS) Protocol Version 1.1 (ietf.org) 中定义,重要的改进点如下:
- TLS 1.2 于 2008 年 8 月在 RFC[4] 5246 中定义。它基于TLS1.1 进行了下面的升级。
- TLS 1.3 于 2018 年 8 月在 RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 (ietf.org) 中定义。它基于早期的 TLS 1.2 规范。与TLS 1.2的主要区别包括:
- 将密钥协议和身份验证算法与密码套件分离。
- 删除对弱椭圆曲线和较少使用的命名椭圆曲线的支持。
- 删除对
MD5
和SHA-224
加密哈希函数的支持。 - 即使使用以前的配置,也需要数字签名。
- 整合HKDF及半瞬时DH建议。
- 用 PSK 和门票替换恢复。
- 支持 1-RTT 握手和对 0-RTT 的初始支持。(和 QUIC 有关)。
- 通过在 (EC)DH 密钥协议期间使用临时密钥来强制实现完全的前向保密。
- 放弃对许多不安全或过时功能的支持,包括压缩、重新协商、非 AEAD 密码、非 PFS 密钥交换(其中包括静态 RSA 和静态 DH 密钥交换)、自定义 DHE 组、EC 点格式协商、更改密码规范协议、Hello 消息 UNIX 时间以及长度字段 AD 输入到 AEAD 密码。
- 禁止 SSL 或 RC4 协商以实现向后兼容性。
- 集成会话哈希的使用。
- 弃用记录层版本号并冻结该编号以提高向后兼容性。
- 将一些与安全相关的算法详细信息从附录移动到规范,并将 ClientKeyShare 降级到附录。
- 添加带有 poly1305 消息身份验证代码的 ChaCha20 流密码。
- 添加 Ed25519 和 Ed448 数字签名算法。
- 添加 x25519 和 x448 密钥交换协议。
- 添加对发送多个 OCSP 响应的支持。
- 加密服务器后的所有握手消息。
许多材料会把TLS/SSL 的SSL放在后面,目的是考虑TLS是目前的主流,放在前面是较为合适的。
所以用现在的眼光看其实SSL早就已经被禁止了,目前主流的HTTP加密传输是基于TLS实现的。此外维基百科上有几张图介绍TLS对比SSL的优势,可以较为直观展示两者的优缺点。
算法
兼容性
数据完整性
数据完整性
网站支持
此外,根据2022年的数据显示,TLS1.3 的覆盖率和 HTTP2.0差不多,但是TLS1.2 经过这几年普及基本全方位支持。
网站支持
TLS/SSL 工作机制
在了解SSL细节之前,我们需要先讲解加密方法:公开密钥加密 和 共享密钥加密。
共享/公开密钥加密
共享密钥加密加密是通信双方持有同一把钥匙加解密信息,所以这种加密方式也叫做对称密钥加密或者共享密钥加密。
共享密钥加密
共享密钥最大的问题是钥匙传输给对方的过程中有可能遭到劫持,一旦传输密钥遭到劫持,共享密钥加密的方式就相当于作废了。
中间人攻击只需要拿到密钥,双方传输加密报文的时候拦截请求数据并且伪造自己的数据,就可以同时“剽窃”双方向的敏感信息。
为了处理这个问题,需要使用公开密钥加密对于共享密钥加密对加密方式进行了改进。
改进方式很简单那就是把钥匙换成一把只能用于加密,这把钥匙可以公开对外使用,而另一把只能用于解密,只有服务端的私钥可以解开公开密钥加密的信息,外部无法通过公钥破解。
如果能在短时间内快速的进行因式分解,那么全世界所有的密码都是透明的。有时候解密不一定是无法破解,而是破解的代价在现实上“不可能”,比如需要花费上千年的时间破解一串密码,等到破解那时候。。。。可能被破解的资源都没了。
公开密钥加密的最大特点是加密和解密的钥匙并不是同一把,两边对于密文的加解密方式不一样,所以这样的加密方式也别叫做 非对称密钥加密。
公开密钥加密
混合加密
HTTPS并不是完全使用公开密钥加密或者共享密钥加密,而是通过两种加密混合的方式进一步提升安全。
共享密钥的问题在于密钥泄露的安全性问题,而公开密钥加密因为加解密的钥匙不是同一把,需要花费更多的操作运算和验证。
HTTPS在设计的过程中基于安全和速度的考虑,最终的决定是在连接握手的过程中使用非对称密钥加密确保安全,在服务器非对称加密验证通过之后,会返回稍后需要共享对称加密的密钥信息。在握手完成之后,在确保安全的前提之下, 使用对称加密的密钥进行共享对称加密的信息交互。
需要注意这里提到的加密认证是单向认证,也就是说只会验证服务端的真实可靠性,服务端无法准确保证客户端是可靠的(但是可以确保传输是安全的)。 客户端认证只在特殊的服务上会用到,大部分服务更多使用服务端单向认证,因为多数服务就是设计给所有人都可以访问的。
HTTPS混合加密
数字证书加密
混合加密的方式看起来很靠谱和安全,但实际上依然存在问题,那就是无法证明公开密钥本身的真实性,为了理解这一点我们可以回顾共享密钥加密的描述图,在其中展示了攻击者在密钥传输的过程中盗取共享密钥的行为。
如果把这一行为替换为盗取公开密钥,则可以在客户端请求的时候劫持替换为攻击者自己的非对称加密密钥,之后的共享加密同样也是,可以被轻易获取。
具体的攻击过程如下:
- 服务端在数字证书认证成功之后,和客户端进行公开密钥加密认证,此时中间人截取到公开密钥,伪造出自己的公钥(同时拥有自己的私钥)以及用于共享之后传给SSL认证的客户端。
- 客户端拿到被替换的服务端公钥认证,将共享加密的密钥通过伪造的密钥加密之后,回传给服务端。
- 中间人继续劫持掉客户端请求,通过自己的私钥解密之后,用自己伪造的共享加密密钥,利用上一次服务端传递的真实公钥,加密之后传给服务端。
- 服务端拿到被加密的假的的共享密钥之后,解密获得中间人的共享加密密钥。
- 原本
在这样的攻击手段之下,为了保证客户端请求的服务器的真实性,采用第三方权威机构认证是合理的。
CA证书
为了解决这个问题,所采用的方式是通过第三方机构数字认证机构(CA,Certificate Authority),加入CA之后整个验证过程如下:
- 服务端运营请求数据认证机构申请公开密钥,数字机构验证请求者的数字认证信息,然后分配给已经签名的密钥,然后把公开密钥放入到公钥证书绑定一起返回。
- 服务器将颁发的数字认证机构的公钥证书发给客户端,使用公开密钥加密通信。这一步也叫数字认证机构传递证书。
- 客户端使用数字证书认证公开密钥,对于数字签名认证,认证通过可以获取两个信息
- 认证服务器的公开密钥的数字认证机构是否合法真实。
- 被认证的服务器公开密钥是否真实。
注意认证机关的公开密钥必须安全传给客户端,否则哪怕是数字认证本身还是有可能被篡改。为了规避这一个问题,许多浏览器会在安装的时候认证机构的公开密钥。
但是浏览器自带证书也有安全隐患,那就是数字认证机构遭到入侵后果不堪设想,历史上也真发生过类似事件。
CA证书
EVSSL证书
证书的作用是保证服务端的公开密钥的真实性,也可以验证服务器是否真实存在。
EV SSL 证书是基于国际标准的认证指导方针颁发的证书。主要的作用是提高网站的认可度。
有时候浏览器如果带HTTPS会出现绿色打勾的字样,这样做是提醒用户网站合法性。
客户端证书
客户端证书通常会出现在安全性要求极高的特殊业务当中,同时客户端本身需要支持SSL证书的开销,但是SSL的客户端证书只能证明请求的机器是没有问题的,但是无法保证
机构信誉
作为数字认证的机构一旦出现问题后果不堪设想,在过去曾经出现过数字认机构被黑客破解的情况,其对于SSL的公信力是一次巨大打击,
OpenSSL
OpenSSL 是可以让用户自己构建一套认证机构的开源程序,但是仅能作为本地使用。
OpenSSL
HTTPS的通信步骤
下面依照SSL的的交互步骤介绍HTTPS的通信过程。
HTTPS的通信步骤
第一次握手:确认支持SSL
- 客户端发送Client Hello 开始SSL通信,
HandShake
就是握手的意思,报文中指定SSL版本和加密组件(加密算法和密钥长度等)。服务器支持SSL通信,返回Server Hello
应答,报文加入SSL的版本以及加密信息。服务器的加密组件需要根据客户端支持的加密通信方式筛选。
第二次握手:服务端证书验证
- 接收到客户端SSL版本以及加密组件信息,服务器支持SSL通信如果则返回
Server Hello
应答。 - 服务器发送
Certificate
报文,报文包含公开密钥证书,证书必须是 x.509 标准格式,包含服务端公钥、服务端域名、签发方信息、有效期等信息。 - 服务器发送
Server Hello Done
表示SSL最初的握手协商已经结束。
第三次握手:客户端确认
- 客户端按照
Client Key Exchange
回应,这个报文会在通信加密中使用Pre-mastersecret
的随机串,这个随机串第一步部分的第三个步骤已经偷偷完成加密了。 - 客户端继续发送
Change Cipher Spec
报文,告知服务器后续使用Pre-master secret
密钥加密通信(共享对称加密)。 - 客户端发送
Finished
报文。在这个报文中包含整个报文回应的校验和,客户端确认是否完成要根据服务器能否认识这一段加密报文为主。
第四次握手:服务端确认
- 服务器同样发送
Change Cipher Spec
报文,表示自己认识客户端的加密信息。 - 服务器同样发送
Finished
报文,SSL连接建立完成。
至此SSL连接建立完成,通信将会受到协商好的共享密钥加密保护,应用层开始进行通信。应用层通信,服务端进行响应。
断开连接
注意在整个SSL四次握手的过程中,应用层发送数据时会附加一种叫做 MAC(Message Authentication Code)
的报文摘要。MAC 能够查知报文是否遭到篡改,从而保护报文的完整性。
最后是书中给的一幅图,了解整个加密过程(个人感觉画的一般,有点乱)
HTTPS通信
CBC 模式(Cipher Block Chaining)又名密码分组链接模式。此模式会把一个明文模块加密处理之后的下一个明文进行XOR运算。重叠之后对于运算结果进行加密处理。对于第一个明文进行加密之后,
最后是IETF 关于TLS协议原文的握手步骤,看起来比较抽象,但是实际上算是最权威的交互信息了,图片展示是TLS1.2的协议原文内容:
除开最后一次的数据交互之外,服务端和客户端需要四次握手才能完成。
也就是说从TCP连接到SSL连接完成,一共需要9次握手才能最终建立一个安全连接,所以其效率可想而知。
为什么不全用HTTPS
- 纯文本通信对比的加密通信消耗更多资源
- 非敏感的HTTPS使用意义和价值不大
- 购买证书的开销和成本。CA证书购买开销不菲,但是对于现在的很多服务器来说是一笔必要开支,虽然有时候非常不合算。
#tjhttp 七、《图解HTTP》- HTTP首部和HTTP协作服务器
7-1. HTTP首部
虽然平时感受不到,但是却是互联网天天在用的东西,这本书花了50多页的内容介绍它,可见它的重要性。
HTTP 首部包含三个部分,报文首部,空行和报文主体,报文首部包含了客户端重要的传输信息,而报文体则是“负荷数据”,包含获取服务器信息需要传递的数据。
首部组成
HTTP 报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。