19 Web编程 HTTP
[TOC]
Web 编程
HTTP 协议
HTTP是万维网的应用层通信协议,Web页面中的数据都是通过HTTP协议进行传输。
HTTP协议自20世纪90年代定义以来至今进行了3次迭代,HTTP 1.1是目前使用最为广泛的版本,最新的版本是2015年发布的HTTP2.0。
HTTP是一种无状态、由纯文本构成的请求-响应(request-response)协议,使用的是客户端/服务器(C/S)模式。
请求-响应是两台计算机进行通信的基本方式,A计算机向B计算机发送请求,B计算机响应A计算机的请求。在C/S模式中,客户端向服务器发起会话,而服务器则负责为客户端提供服务。在HTTP协议中,客户端也被称为用户代理(user-agent),而服务器通常被称为Web服务器。
HTTP是无状态协议,它唯一知道的就是客户端会向服务器发送请求,而服务器会响应客户端的请求,并且后续发生的请求对之前发生过的请求一无所知。相对的,FTP、Telnet这类面向连接的协议则会在客户端和服务器之间创建一个持续存在的数据通道。,HTTP1.1协议也可以通过持久化连接提升性能,可以做到一次连接多次请求。
HTTP 请求报文
HTTP请求报文由一系列有顺序的文本行组成:
(1):请求行(request-line):请求方式 /URL参数 协议版本
(2):任意个数的请求头(header):每个请求头独占一行,如 Host:请求目标IP:PORT,Connection:keep-alive 保持连接。
(3):空行:空行隔开请求体和请求头。
(4):请求体(body):POST、PUT等请求方法数据存在请求体中。
Chrome 发起的请求报文
http://127.0.0.1:17070/?data=testData
HTTP 请求方法
请求行第一行的第一个单词就是表明请求方法,它表明客户端想对资源执行的操作。
HTTP1.1新添加了PUT、DELETE、OPTIONS、TRACE、CONNECT,加上原有的GET、POST、HEAD方法,一共有8种,还允许自定义方法。
HTTP1.1要求必须实现的方法只有GET和HEAD方法。
安全的请求方法
HTTP请求方法只要求服务器提供信息而不会对服务器的状态做任何修改,那这个方法就是安全的,GET、HEAD、OPTIONS、TRACE都不会对服务器的状态进行修改,所以它们都是安全的方法。
POST、PUT、DELETE都能够对服务器的状态进行修改,如处理POST请求时服务器存储的数据就可能会发生变化,因此这些方法都是不安全的。
方法
幂等
安全
作用描述
GET
是
是
查询资源
HEAD
是
是
与GET方法的作用类似,但是返回的响应中没有具体的内容,用于获取响应头。
POST
否
否
新增资源。
PUT
是
否
更新资源。
DELETE
是
否
删除资源。
OPTIONS
是
是
获得针对特定资源所支持的HTTP请求方法,也可向web服务器发送‘*’的请求来测试服务器的功能性。
PATCH
否
否
更新资源,是对 PUT 方法的补充,用来对已知资源进行局部更新 。
CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器,通常用于设置SSL隧道以开启HTTPS功能。
TRACE
返回请求本身,主要用于测试或诊断。
幂等的请求方法
一个HTTP请求使用相同的数据进行第二次调用不会对服务器的状态造成任何改变,那这个HTTP请求使用的请求方法就是幂等的(idempotent)。
安全的方法不会修改服务器状态,所以它们都是幂等的。
PUT和DELETE虽然不安全,但却是幂等的,因为它们在第二次调用时都不会改变服务器状态,服务器在执行第一个PUT请求后资源已经被更新或创建了,第二次PUT请求只会执行服务器已经执行过的动作。而DELETE对同一个资源执行第二次删除如果第一次已经成功,因为资源已经不存在那就会返回一个错误,这个请求也不会对服务器状态做出改变。
重复的POST请求是否改变服务器状态是由服务器自身业务逻辑决定的,所以POST 方法既不安全也非幂等。
HTTP 常见请求头 Request Header
HTTP 请求方法定义了发送请求的客户端想要执行的动作,而HTTP请求头则记录了与请求本身以及客户端有关的信息。请求头每行由任意多个用冒号分隔的纯文本键值对组成,最后以回车和换行结尾。
HTTP 1.1 RFC 7231标准中,对主要的一些请求字段进行了标准化。
HTTP 请求头大部分是可选的,Host 请求头是 HTTP1.1 强制要求的。请求方法不同还会有些特有字段,如请求有请求体还需要有Content-Length请求体内容长度字段。
字段
作用描述
示例
Host
指定请求的服务器的域名和端口号
Host: 127.0.0.1:17070
Accept
指定客户端能够接收的内容类型
Accept: text/plain, text/html,application/json
Accept-Charset
浏览器可以接受的字符编码集
Accept-Charset: iso-8859-1
Accept-Encoding
指定浏览器可以支持的web服务器返回内容压缩编码类型
Accept-Encoding: compress, gzip
Accept-Language
浏览器可接受的语言
Accept-Language: en,zh
Connection
表示是否需要持久连接(HTTP 1.1默认进行持久连接)
Connection: close
Cookie
HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器
Cookie: $Version=1; Skin=new;
Content-Length
请求体的内容长度
Content-Length: 100
Content-Type
请求体内容的类型
Content-Type: application/x-www-form-urlencoded
User-Agent
User-Agent的内容包含发出请求的用户信息
User-Agent: Mozilla/5.0 (Linux; X11)
Referer
发起请求的所在地址
Referer: http://...
HTTP 响应
HTTP响应报文是对HTTP请求的回复,和HTTP请求一样,HTTP响应也是由一系列文本行组成。
状态行:协议版本 响应状态码 状态说明短语。
任意个数响应头:响应header,每个1行,行内参数用;隔开。
空行:空行隔开响应内容和响应头
响应内容:数据。
响应状态码
HTTP响应中的状态码表明响应的类型,HTTP响应状态码共有5种类型,分别以不同数字作为前缀。
状态码有很多,还可以自定义状态码。
常见状态码:
1XX
消息
情报状态码。请求已被接受,需要继续处理。
100 Continue
服务器已经接收到请求头,并且客户端应继续发送请求主体,或者如果请求已经完成,忽略这个响应。
2XX
成功
成功状态码。已经成功对请求进行处理。
200 OK
请求已成功,请求所希望的响应头或数据体将随此响应返回。
201 Created
请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。假如需要的资源无法及时创建的话,应当返回'202 Accepted'。
3XX
重定向
这类状态码代表需要客户端采取进一步的操作才能完成请求。
301 Moved Permanently
被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。
302 Found
要求客户端执行临时重定向。由于重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。除非真是临时重定向,其他的情况还是使用301。
4XX
客户端错误
客户端看起来可能发生了错误,妨碍了服务器的处理。
400 Bad Request
由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。
403 Forbidden
服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。
404 Not Found
请求失败,请求所希望得到的资源未被在服务器上发现,但允许用户的后续请求。
5XX
服务器错误
表示服务器无法完成明显有效的请求。
500 Internal Server Error
通用错误消息,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
502 Bad Gateway
作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
HTTP 常见响应头 Response Header
响应头跟请求头一样,都是纯文本键值对组成,最后空行隔开响应头和响应数据。
字段
作用描述
Allow
服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding
文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length
表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type
表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
Date
当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires
应该在什么时候认为文档已经过期,从而不再缓存它?
Last-Modified
文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location
表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh
表示浏览器应该在多少时间之后刷新文档,以秒计。还可以通过setHeader("Refresh", "5;URL=http://host/path")让浏览器读取指定的页面。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
Server
服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie
设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
WWW-Authenticate
客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
TCP 处理、响应HTTP请求
HTTP是基于TCP封装的应用层协议,完全可以使用 TCP 处理、响应 HTTP 请求。
访问 http://localhost:17070/ 模拟一次HTTP请求响应。
HTTPS 协议
HTTP 存在的问题
数据明文传输,存在被窃听截取隐患。
数据的完整性未校验,存在被篡改隐患。
HTTPS (Hypertext Transfer Protocol Secure:超文本传输安全协议),HTTPS 解决数据传输安全问题的方案就是使用混合加密算法,对称加密和非对称加密的混合使用。传输的数据使用对称加密,对称加密秘钥由公钥加密生成。
HTTPS 是网络安全通信的传输协议,是基于HTTP的扩展,是HTTP的安全版,HTTP下加入SSL层,HTTPS的安全基础是SSL。
HTTPS 经由 HTTP 进行通信,但利用 SSL/TLS 来加密数据包,因此协议的数据格式和HTTP是一样的,有一样的请求头、响应头,也是用空行隔开头部和请求体,只是HTTPS是将这些数据加密的。
HTTPS 开发的主要目的,是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

HTTP和HTTPS的对比图
HTTPS 默认工作在 TCP 协议443端口,它的工作流程一般如以下方式:
1、TCP 三次同步握手
2、客户端验证服务器数字证书
3、DH 算法协商对称加密算法的密钥、hash 算法的密钥
4、SSL 安全加密隧道协商完成
5、网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改。

HTTPS数据传输流程
HTTPS 和 HTTP 的区别
HTTPS 和 HTTP 的区别
HTTP和HTTPS使用的连接方式不同,默认端口也不一样,HTTP是80,HTTPS是443。
HTTPS是HTTP协议的安全版本,HTTP是明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP)数据传输过程是加密的,安全性较好。
使用 HTTPS 协议需要到 CA(Certificate Authority,数字证书认证机构)申请证书,一般需要一定费用。
HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS 除了TCP三次握手还要进行 TLS 握手,尽管在TLS第一次握手做了优化数据包是在TCP第三次握手时发送,但是还有三次握手。
HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
HTTPS的优点
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比HTTP协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
搜索引擎更青睐采用HTTPS加密的网站。
HTTPS的缺点
HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%。
HTTPS连接缓存不如HTTP高效,会增加资源消耗。
SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大。
SSL证书通常需要绑定IP。
HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击还是会发生。
申请SSL证书是收费的,功能越强大的证书费用越高。
SSL、TLS 和 HTTPS
SSL(Secure Socket Layer,安全套接字层)是一种通过公钥基础设施为通信双方提供数据和身份验证的协议,通信双方通常是客户端和服务器。
HTTPS是在SSL之上的HTTP,是在SSL/TLS连接的上层进行HTTP通信。
HTTPS需要使用SSL证书来实现数据加密以及身份验证。
SSL证书存储在服务器,它使用X.509格式进行格式化数据,数据中包含了公钥以及其它一些相关信息,比如证书颁发机构信息、公司信息和证书有效期等。为了保证证书的可靠性,证书一般由证书分发机构签发。
服务器在接收到客户端的请求后,会将证书和响应一并返回客户端,而客户端在确认证书的真实性后,就会生成一个随机秘钥,并使用证书中的公钥对随机秘钥进行加密,此次加密产生的对称秘钥就是客户端和服务器在通信时负责对数据进行加密的实际秘钥。
SSL最初由Netscape公司开发,之后由IETF互联网工程任务组接手并将其改名为TLS(传输层安全协议)。
SSL 和 TLS 协议可以为通信双方提供识别和认证通道,从而保证通信的机密性和数据完整性。
TLS 协议是从Netscape SSL 3.0协议演变而来的,不过这两种协议并不兼容,SSL 已经逐渐被 TLS 取代。
TLS 握手是启动 HTTPS 通信的过程,类似于 TCP 建立连接时的三次握手。在 TLS 握手的过程中,通信双方交换消息以相互验证,相互确认,并确立它们所要使用的加密算法以及会话密钥 (用于对称加密的密钥)。TLS 握手是 HTTPS 通信的基础部分。
TLS 握手的目的是建立安全连接,通信双方在TLS握手过程中发生了:
确定双方通信所使用的的 TLS 版本 (例如 TLS1.0, 1.2, 1.3等等)。
确定双方所要使用的密码组合。
客户端通过服务器的公钥和数字证书上的数字签名验证服务端的身份。
生成会话密钥,该密钥将用于握手结束后的对称加密。
TLS 和 SSL 的区别
TLS(Transport Layer Security Protocol)安全传输层协议
SSL(Secure Socket Layer)安全套接字层
版本号差异:TLS记录格式与SSL记录格式相同,但版本号的值不同,TLS的版本1.0使用的版本号为 SSLv 3.1。
报文鉴别码差异:SSL v3.0 和 TLS 的MAC算法及MAC计算的范围不同。TLS使用了RFC-2104定义的HMAC算法。SSL v3.0 使用了相似的算法,两者差别在于 SSL v3.0 中,填充字节与密钥之间采用的是连接运算,而HMAC算法采用的是异或运算。但是两者的安全程度是相同的。
伪随机函数:TLS使用了称为PRF的伪随机函数来将密钥扩展成数据块,是更安全的方式。
报警代码:TLS支持几乎所有的 SSL v3.0 报警代码,而且TLS还补充定义了很多报警代码,如解密失败(decryption_failed)、记录溢出(record_overflow)、未知CA(unknown_ca)、拒绝访问(access_denied)等。
密文族和客户证书:SSL v3.0 和 TLS 存在少量差别,即TLS不支持Fortezza密钥交换、加密算法和客户证书。
certificate_verify和finished消息:SSL v3.0 和 TLS 在用certificate_verify和finished消息计算MD5和SHA-1散列码时,计算的输入有少许差别,但安全性相当。 加密计算:TLS 与 SSL v3.0 在计算主密值(master secret)时采用的方式不同。
填充:用户数据加密之前需要增加的填充字节。在SSL中,填充后的数据长度要达到密文块长度的最小整数倍。而在TLS中,填充后的数据长度可以是密文块长度的任意整数倍(但填充的最大长度为255字节),这种方式可以防止基于对报文长度进行分析的攻击。
TLS的主要增强在安全上
TLS的主要目标是使SSL更安全,并使协议的规范更精确和完善。TLS 在SSL v3.0 的基础上,使用了更安全的MAC算法、更严密的警报、“灰色区域”规范的更明确的定义、改进安全性。
对于消息认证使用密钥散列法:TLS 使用“消息认证代码的密钥散列法”(HMAC),当记录在开放的网络(如因特网)上传送时,该代码确保记录不会被变更。SSL v3.0 还提供键控消息认证,但HMAC比SSL v3.0 使用的(消息认证代码)MAC 功能更安全。
增强的伪随机功能(PRF):PRF生成密钥数据。在TLS中,HMAC定义PRF。PRF使用两种散列算法保证其安全性。如果任一算法暴露了,只要第二种算法未暴露,则数据仍然是安全的。
改进的已完成消息验证:TLS和SSLv3.0都对两个端点提供已完成的消息,该消息认证交换的消息没有被变更。然而,TLS将此已完成消息基于PRF和HMAC值之上,这也比SSLv3.0更安全。
一致证书处理:与SSLv3.0不同,TLS试图指定必须在TLS之间实现交换的证书类型。
特定警报消息:TLS提供更多的特定和附加警报,以指示任一会话端点检测到的问题。TLS还对何时应该发送某些警报进行记录。
TLS 握手详细过程
TLS的五次握手
第一次:TLS第一次握手做了优化,在TCP第三次握手时一并完成,因此实际建立连接新增的交换数据包是4次,这次握手是客户端向服务器发送(客户端支持的TLS协议版本、加密算法组合、随机字符串C)。
第二次:TLS第二次握手,这次握手是服务器响应客户端(SSL证书、服务器选择的加密算法、随机字符串S)。
第三次:TLS第三次握手,客户端再次生成一个随机字符串C2并使用公钥加密后发送给服务器。
第四次:TLS第四次握手,客户端向服务器发送FINISHED。
第五次:TLS第五次握手,服务器响应客户端FINISHED。
握手完成,双方使用 【随机字串C1+随机字串S+随机字串C2】+ 服务器选择算法生成对称加密秘钥,以后双发数据传输使用这个秘钥进行对称加密,对称加密算法使用服务器选择的加密算法。
加密算法组合:非对称加密(即证书的密钥对)使用的加密算法、生成对称秘钥的数字摘要加密算法、数据传输的对称加密算法。
SSL / TLS 握手详细过程
1:客户端通过发送消息向服务器发起握手请求,该消息包含了客户端所支持的 TLS 版本和密码组合以供服务器进行选择,还有一个"client random"随机字符串。
2:服务器客户端进行回应,回应数据中包含了数字证书,服务器选择的密码组合和"server random"随机字符串。
3:客户端对服务器发来的证书进行验证,确保对方的合法身份。检查数字签名、验证证书链、检查证书的有效期、检查证书状态 (撤回状态代表证书已失效)。
4:客户端向服务器发送另一个使用公钥加密过的随机字符串 "premaster secret" ,这个服务器使用对应的私钥解密。
5:服务器使用私钥解密客户端发来的随机字符串 "premaster secret"。
6:客户端和服务器均使用 客户端随机字符串 client random、服务器随机字符串 server random 和 预主密钥 premaster secret ,并通过相同的算法生成相同的共享密钥 KEY。
7:客户端发送经过共享密钥 KEY加密过的"finished"信号。
8:服务器发送经过共享密钥 KEY加密过的"finished"信号。
9:达成安全通信:握手完成,双方使用对称加密进行安全通信。

TLS握手过程
TLS 握手过程中的一些重要概念
数字证书 (digital certificate):在非对称加密通信过程中,服务器需要将公钥发送给客户端,在这一过程中,公钥很可能会被第三方拦截并替换,然后这个第三方就可以冒充服务器与客户端进行通信,这就是传说中的“中间人攻击”(man in the middle attack)。解决此问题的方法是通过受信任的第三方交换公钥,具体做法就是服务器不直接向客户端发送公钥,而是要求受信任的第三方,也就是证书认证机构 (Certificate Authority, 简称 CA)将公钥合并到数字证书中,然后服务器会把公钥连同证书一起发送给客户端,私钥则由服务器自己保存以确保安全。数字证书一般包含以下内容:
证书所有者的公钥
证书所有者的专有名称
证书颁发机构的专有名称
证书的有效起始日期
证书的过期日期
证书数据格式的版本号
序列号,这是证书颁发机构为该证书分配的唯一标识符
... ...
数字签名 (digital signature)是为了确保数据发送者的合法身份,也可以确保数据内容未遭到篡改,保证数据完整性。与手写签名不同的是,数字签名会随着文本数据的变化而变化。具体到数字证书的应用场景,数字签名的生成和验证流程如下:
服务器对证书内容进行信息摘要计算 (常用算法有 SHA-256等),得到摘要信息,再用私钥把摘要信息加密,就得到了数字签名
服务器把数字证书连同数字签名一起发送给客户端
客户端用公钥解密数字签名,得到摘要信息
客户端用相同的信息摘要算法重新计算证书摘要信息,然后对这两个摘要信息进行比对,如果相同,则说明证书未被篡改,否则证书验证失败
证书链 (certificate chain):
证书链,也称为证书路径,是用于认证实体合法身份的证书列表,具体到 HTTPS 通信中,就是为了验证服务器的合法身份。之所以使用证书链,是为了保证根证书 (root CA certificate)的安全,中间层可以看做根证书的代理,起到了缓冲的作用。
证书链从根证书开始,并且证书链中的每一级证书所标识的实体都要为其下一级证书签名,而根证书自身则由证书颁发机构签名。客户端在验证证书链时,必须对链中所有证书的数字签名进行验证,直到达到根证书为止。
密码规范和密码组合 (CipherSpecs 和 CipherSuites):
通信双方在安全连接中所使用的算法必须符合密码安全协议的规定,CipherSpecs 和 CipherSuites 正好定义了合法的密码算法组合。CipherSpecs 用于认证加密算法和信息摘要算法的组合,通信双方必须同意这个密码规范才能进行通信。而 CipherSuites 则定义了 SSL / TLS 安全连接中所使用的加密算法的组合,该组合包含三种不同的算法:
握手期间所使用的的密钥交换和认证算法 (最常用的是 RSA 算法)
加密算法 (用于握手完成后的对称加密,常用的有 AES、3DES等)
信息摘要算法 (常用的有 SHA-256、SHA-1 和 MD5 等)
HTTP2 协议
HTTP2 是 HTTP 协议的新版本,这一版本更注重性能。
HTTP2协议是SPDY/2协议改进而来,SPDY/2是Google为了传输Web内容而开发的一种开放的网络协议。
与使用纯文本方式的HTTP1.1不同,HTTP2是一种二进制协议,语法分析更为高效,更为紧凑和健壮。
和HTTP1.1在一个网络连接里面每次只能发送单个请求的做法不同,HTTP2是完全多路复用的,多个请求和响应可以在同一时间内使用同一个连接。HTTP2还会对头部进行压缩以减少需要传送的数据量,并允许服务器将响应推送至客户端,这些措施都能够有效地提升性能。
HTTP2对通信性能进行了优化,但它并没有对HTTP协议本身的语法进行修改,在HTTP2中HTTP的方法和状态码等都被保留未做改变。
在 Go 1.6 版本之后,使用 HTTPS 时会自使用HTTP2。
握手方式 h2、hc2
HTTP2协议握手分h2、hc2种方式
h2要求必须使用TLS加密,在TLS握手期间会顺带完成HTTPS/2协议的协商,如果协商失败(比如客户端不支持或者服务端不支持),则会使用HTTPS/1继续后续通讯。
h2c不使用TLS,而是多了一次基于HTTP协议的握手往返来完成向HTTP/2协议的升级。
HTTP2协议在安全上与HTTP1协议一样,同样可选择HTTP明文传输,也可以是HTTPS加密传递。
不过在google设计HTTP2协议的时候,偏向了加密传输,以至于Go 1.6 中默认支持的HTTP2协议包却没有办法实现非加密HTTP2通信。
在实现中,HTTP2分为两个类型,加密的称为HTTP2/h2,非加密的称为HTTP2/h2c。
在使用中,HTTP2/h2一般都有官方标准的实现,而HTTP2/h2c则支持力度较小。
而由于加密HTTP2/h2由于在TLS握手阶段,可能消耗约~10ms时间,在有些场合是非常大的开销,所以还可能要用到HTTP2/h2c实现方式。比如在grpc中,服务器之间通信要求更高,就自带了一个HTTP2/h2c的实现。
HTTP2 相比 HTTP1 的改进
HTTP 2.0 相比于 HTTP 1.1 ,大幅度提升了网络通信的性能。
在与 HTTP/1.1 完全语义兼容的基础上,HTTP2 进一步减少了网络延迟。
解决连接无法复用:
HTTP 1.1 协议下浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞。这也是为何一些网站会使用多个静态资源CDN域名的原因之一,目的之一是解决浏览器针对同一域名的请求限制阻塞问题。
HTTP 1.1 初步解决了连接无法复用问题,默认设置 Connection:Keep-Alive 请求头,可以在一定时间内复用连接,具体复用时间的长短可以由服务器控制,一般在15s左右,这样避免了服务器频繁建立、销毁TCP连接,但是要同时发起多个请求还是要开启多个连接,而HTTP2的多路复用优化了这一缺点。
HTTP2 的多路复用(Multiplexing) 允许同时通过单一的 HTTP/2 连接发起多个请求。因此 HTTP2 可以很容易实现多流并行而不用依赖建立多个 TCP 连接。
HTTP1优化的是长连接复用,HTTP2升级的是连接多路复用。HTTP1长连接复用是一个连接多次请求,但是是排队的,一个请求处理完成才能继续处理下一个请求。多路复用是一个连接可以并发多个请求,不需要一个一个排队。
多路复用(连接共享)
HTTP 性能优化的关键是降低延迟。HTTP2 通过让所有数据流共用同一个连接,可以更有效地使用TCP连接。多路复用(MultiPlexing)就是HTTP2.0降低延迟的手段。
协议中的stream id就是用作多路复用机制的。一个request请求对应一个stream并分配一个id,这样一个连接上可以有多个stream,每个stream的frame可以随机的混杂在一起,接收方可以根据streamid将frame再归属到各自不同的request请求里面。
连接共享之后,需要优先级和请求依赖的机制配合才能解决关键请求被阻塞的问题。HTTP2.0里的每个stream都可以设置优先级(Priority)和依赖(Dependency)。优先级高的stream会被server优先处理和返回给客户端,stream还可以依赖其它的sub streams。优先级和依赖都是可以动态调整的。动态调整在有些场景下很有用,假设用户在用你的app浏览商品的时候,快速的滑动到了商品列表的底部,但前面的请求先发出,如果不把后面的请求优先级设高,用户当前浏览的图片要到最后才能下载完成,显然体验没有设置优先级好。同理依赖在有些场景下也有妙用。
新的二进制格式:
HTTP1 是明文协议,其格式由三部分组成:请求行,请求头,空行,请求体。HTTP1 的解析是基于文本,基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多。
HTTP2.0的协议解析采用二进制格式,实现方便且健壮,二进制只认0和1的组合。
HTTP2.0的格式定义更接近tcp层的方式。
length定义了整个frame(帧)的开始到结束。
type定义frame的类型(一共10种)。
flags用bit位定义一些重要的参数。
stream id用作流控制。
payload是request的正文。
Length
3字节
帧负载长度。
Type
1字节
当前帧类型。
Flags
1字节
帧类型标识。
R
1位
保留位。
Stream ID
31位
每个流唯一编号。
Payload
长度可变
帧内容,Length字段就是Payload的长度。

HTTP2.0和HTTP1的格式对比图
HTTP2协议的格式和HTTP1完全不同,但HTTP2并没有改变HTTP1的语义,只是把原来HTTP1的Header和Body部分用frame重新封装了一层。

HTTP1和HTTP2协议数据包关系图
服务器主动推送资源
HTTP1 不支持服务器主动推送资源给客户端,都是由客户端向服务器发起请求后,才能获取到服务器响应的资源。
比如,客户端通过 HTTP1 请求从服务器那获取到了 HTML 文件,而 HTML 可能还需要依赖 CSS 来渲染页面,这时客户端还要再发起获取 CSS 文件的请求,需要两次消息往返。
在 HTTP2 中,客户端在访问 HTML 时,服务器可以直接主动推送 CSS 文件,减少了消息传递的次数。
客户端发起的请求使用奇数号 Stream,服务器主动推送使用偶数号 Stream。服务器在推送资源时,会通过 PUSH_PROMISE 帧传输 HTTP 头部,并通过帧中的 Promised Stream ID 字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体,这个过程可以是并发的。
Header压缩
HTTP1中,如果一个页面有100个资源需要加载,而每一次请求都有1kb的消息头(Cookie、user agent等等), 则至少需要多消耗100kb来获取这些消息头,而且每次都要重复发送。因此对头部进行压缩很重要。
HTTP2.0 首先通过编码来减少传输的Header大小,通讯双方各自缓存一份Header Fields表,既避免了重复Header的传输,又减小了需要传输的大小,这样差量更新HTTP头部,大大降低因头部传输产生的流量。
高效的压缩算法可以很大的压缩Header,减少发送包的数量从而降低延迟。SPDY/2使用的是gzip压缩算法,但后来出现的BREACH和CRIME攻击使得即使走ssl的SPDY也可以被破解内容,最后综合考虑采用的是HPACK压缩算法。
Go 服务端开启h2
GO的http库默认支持HTTP/2协议,只要使用TLS则会默认启动HTTP/2特性。
http库在设计API时并没有支持用户使用h2c,而是鼓励使用h2。
只要使用TLS,则http库就会默认进行HTTPS/2协商,协商失败则蜕化为HTTPS/1。
ListenAndServeTLS(证书文件路径, 私钥文件路径)】 使用TLS启动HTTP2/特性。
Go 客户端访问HTTP2
一般情况下客户端都是浏览器,在一些特殊情况下使用Go编写客户端代码访问HTTP2服务器也会有。
客户端要配置Transport,Transport是底层的连接管理器,包括了协议的处理能力。
HTTP3
HTTP2 是基于 TCP 协议来传输数据的。
HTTP2 通过 Stream 的并发能力,解决了 HTTP1 队头阻塞的问题,但是 HTTP2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。
TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前1个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP2 应用层才能从内核中拿到数据,这就是 HTTP2 队头阻塞问题。
HTTP3 协议放弃 TCP 协议,使用 UDP 协议作为传输层协议,解决了这一问题,但是HTTP3这个协议在2018年才制定,离普及还有一段时间。
HTTP3 Header 压缩
HTTP3的 Header 压缩使用的压缩算法是QPack。HPACK算法依赖于数据流的有序交付,由于HTTP/3的数据流之间可能乱序(因为基于UDP传输),所以该算法需要修改才能使用。QPACK可被视作适用于QUIC版本的HPACK。
HTTP3 Stream 数据流
数据流(Streams)在QUIC中提供了一个轻量级、有序的字节流的抽象化。
QUIC中有两种基本的数据流类型:从发起者到对等端(Peer)的单向数据流。双向均可发出数据的双向数据流。
连接端点的任意一方都可以建立这两种数据流,数据流之间可并行、交错地传输,并且可以被取消。
通过QUIC发送数据需要建立一个或多个数据流。
流量控制:每个数据流都有独立的流量控制,端点可以通过此实现内存控制和反压(back pressure)。数据流的创建本身也有流量控制,连接双方可以声明最多愿意创建几个流ID。
流标识符:数据流通过一个无符号的62比特整数标识,也称流ID。流ID的最低2位比特用于识别流的类型(单向或双向)和流的发起者。 流ID的最低1位比特(0x1)用于识别流的发起者。客户端发起双数(最低位置0)流,服务器发起单数(最低位置1)流。 第2个比特(0x2)识别单/双向流。单向流始终置1,双向流则置0。
流并发:QUIC允许任意数量的并发流。端点通过闲置最大流ID来控制并发活动的传入流数量。每个端点指定自己的最大流ID数,并只对对等端端点有效。
收发数据:端点使用流来收发数据,这是流的最终用途。QUIC数据流是有序的字节流抽象。但是,不同流之间是无序的。
流优先度:如果正确设置了各流的优先度,流复用机制可以显著提升应用的效率。使用其他多路复用协议(如HTTP/2)的经验表明,有效的优先度划分策略对效率具有显著的正面影响。QUIC本身没有提供交换优先度信息的报文。 接收优先度信息依赖于使用QUIC的应用层。应用层可以定义所有复合其语义的优先度方案。 基于QUIC使用HTTP/3时,优先度信息在HTTP层完成。
HTTP3 传输层协议 QUIC
QUIC连接是两个QUIC端点之间的单次会话(conversation)过程。QUIC建立连接时,加密算法的版本协商与传输层握手合并完成,以减小延迟。在连接上实际传输数据时需要建立并使用一个或多个数据流。
QUIC是一个传输层协议,在该协议之上可以运行其他应用层协议。初始的应用层协议是HTTP/3(h3)。传输层协议负责连接和数据流处理。
连接ID(Connection ID) 每个连接过程都有一组连接标识符,或称连接ID,该ID用以识别该连接。每个端点各自选择连接ID。每个端点选择对方使用的连接ID。 连接ID的基本功能是确保底层协议(UDP、IP及其底层协议)的寻址变更不会使QUIC连接传输数据到错误的端点。 利用连接ID的优势,连接可以在IP地址和网络接口迁移的情况下得到保持——而这TCP永远做不到。举例来说,当用户的设备连接到一个Wi-Fi网络时,将进行中的下载从蜂窝网络连接转移到更快速的Wi-Fi连接。与此类似,当Wi-Fi连接不再可用时,将连接转移到蜂窝网络连接。
端口号:QUIC基于UDP建立,因此使用16bit的UDP端口号字段来区分传入的不同连接。
版本协商:客户端的QUIC连接请求会告知服务器所希望使用的QUIC协议版本,服务器端会回复一系列支持的版本供客户端选择。
HTTP3 TLS连接
在初始的数据包建立连接之后,连接发起者会马上发一个加密的帧以开始安全层握手。安全层使用TLS 1.3协议。
在QUIC中,没有方法或机制避免使用TLS连接。该设计旨在使中间设备难以篡改数据包,防止协议僵化。
QUIC 反射攻击
QUIC 握手过程是不对称的,如果发送数据包时的原地址是伪造的,不是真正的地址,会引起放大攻击,特别是第一次请求时,客户端只需要发送几个字节的信息到服务器,而服务器则需要把证书等很多数据响应给客户端,这个不对称的机会造成了放大。
QUIC定义了两个规则和机制来限制反射攻击:客户端发送Initial包,即第一个数据包时,其长度必须在 1200 bytes以上,不足部分用 Padding 帧填充,同时,当服务端不确定客户端可靠性时,可以发送 Retry 包要求客户端再次提供验证信息。
HTTP各版本层次对比图

HTTP各版本层次对比图
Last updated