HTTP——需要知道的协议

本文属于原创文章,转载请注明–来自桃源小盼的博客

笔者语

作为一个程序员,当我们访问一个接口,服务器接收到并返回结果,那么中间的流程是怎么处理的呢?这个请求是如何到达服务器,服务器又是怎样返回内容的?

如果没有HTTP协议,接口请求具体实现的细节, 都需要每个客户端和服务器各自约定和实现,而自己的规则,又不能适用于别人。这给开发带来了极大的不便,HTTP就是为此而设计的。HTTP协议用来约定双方的行为规范,让相关开发者按照相同的规则来开发网站和工具。

HTTP协议就像发快递时填写的发货单,规定必须填写收货人,地址和手机号码,只有这样才能准确送给收货人。HTTP则是规定了如何在两台电脑间发送和接收超文本。

起源历史

1980年,蒂姆·伯纳斯·李在CERN(欧洲核子研究组织)时,为了方便各地研究人员共享信息,提出了一个设想,”借助于超文本,连接成可互相浏览的WWW(万维网)项目”。

到了1989年,伯纳斯·李看到了将超本文与互联网结合的机会,那时已经有了在电脑上显示信息的超文本系统,也有了域名系统和TCP/IP网络传输协议。伯纳斯·李又为此设计制作世界上第一个网页浏览器和网页服务器,将这一切组合起来,就能实现浏览处于世界任何地方服务器上的超文本信息。在这个过程中,由于TCP/IP协议族中没有适合传输超文本的协议,李博士又发起了HTTP(超文本传输协议)的提议。

于是在1989年,HTTP协议诞生了。现如今最广泛使用的协议版本是在1999年制定的HTTP 1.1。

蒂姆·伯纳斯·李在2017年4月5日,获得了2016年度图灵奖,被誉为万维网之父。他发明了浏览器, HTTP,HTML,URI等一系列相关的万维网技术。

TCP/IP协议族

现实世界中的各行各业都有自己的行业规则,违反规则,步履艰难,而尊重规则,便如鱼得水。网络世界也需要各种各样的规则,TCP/IP协议族就是这些规则的总称。而HTTP协议是其中的一种,负责传输超文本(HyperText)。

TCP/IP协议族一共分为四层,包含不同的协议。应用层、传输层、网络层和链路层。

这张图简单描述了,打开一个网站背后都发生了什么?

http-1

  1. 通过DNS协议,获得访问域名对应的服务器IP地址。DNS协议属于应用层。
  2. 紧接着用到了HTTP协议,将生成的HTTP报文发送给服务器。HTTP协议属于应用层。
  3. 数据在网络中的传输是十分重要的,为了保证数据传输的稳定性和完整性,制定了TCP协议,它将数据分割成报文段,按序号传输。TCP协议属于传输层。
  4. 客户端发起的请求,怎么在众多机器中找到对应那一台,需要IP协议来寻找一条路径。IP协议属于网络层。
  5. 最后服务器收到了请求,把响应内容按照之前的步骤,返回给客户端。

HTTP协议概述

客户端发出请求报文,服务器收到请求,经过处理,把响应报文返回客户端,连接断开,一次请求结束。HTTP协议是无状态协议。

在1990年W3C发布了第一个HTTP/0.9版本,这个版本只支持GET请求。

1996年发布了HTTP/1.0版本,这是第一个广泛使用的版本,支持了多媒体类型和各种HTTP首部字段。

但真正应用至今的是在1999年发布的HTTP/1.1版本,它修复了一些结构的缺陷,并引入了性能优化的措施。本文以下的内容都以HTTP/1.1为基础展开。

我们用chrome浏览器打开http://www.w3c.org这个网站。打开开发者工具的network项,看看第一个请求的详细信息。

http-2

请求报文由请求地址、请求方法、协议版本、首部字段和内容实体。

GET /index.html HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6)
Host: w3c.org

响应报文由状态码及解释短语、协议版本、首部字段和响应实体。

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 137582
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

HTTP请求方法

HTTP/1.1 支持多种请求方法,最常用的还是get和post方法。

HTTP通信是建立在TCP连接的基础上,早期版本每次通信都需要重新连接、断开TCP。所以在HTTP/1.1下,实现了在一次TCP连接中进行多次HTTP通信的能力,大大提高了服务器的响应速度。同时也支持并行发送请求,一般浏览器是支持同时6个连接。

GET

gee方法是安全的请求方法,获取已经存在的资源或者是查询一些数据,通常会把请求参数拼接在url中。

head方法也是安全的,但它与get的不同在于,它不会返回响应实体内容,只返回响应首部。一般会用来确认请求url的有效性。

POST

post方法会把请求内容放在请求实体中,而不是拼接在url中。所以一般查询信息用GET方法,提交表单数据使用post方法。

OPTIONS

查询指定url资源支持的请求方法,例如支持get和head。

TRACE

一般请求发出后,会经过多层代理服务器,这个方法就是用来确认请求发出后发生的一系列操作。但会引起跨站追踪攻击,一般不用。

PUT与DELETE

put是用来往服务器上传文件,而delete就是删除服务器上的文件。但是这两个方法没有验证机制,会产生不安全问题,一般服务器都不做支持。

状态码

状态码用来表示服务器返回的请求结果。由三位数字加解释短语组成,例如 200 ok。
虽然状态码有很多,但是也可分门别类,并不需要掌握所有,也对返回的结果有个大致的了解。

状态码 响应类别 原因短语
1xx 信息性状态码(Informational) 服务器正在处理请求
2xx 成功状态码(Success) 请求已正常处理完毕
3xx 重定向状态码(Redirection) 需要进行额外操作以完成请求
4xx 客户端错误状态码(Client Error) 客户端原因导致服务器无法处理请求
5xx 服务器错误状态码(Server Error) 服务器原因导致处理请求出错

当我们知道了,首位数字是定义状态码的类型后,理解更多的状态码也就简单起来。
下面,再详细介绍一些常见的状态码。

200 OK

这个是最常见的,表示请求在服务器被正确处理了。

204 No Content

请求在服务器端被正确处理了,但是返回的响应报文中没有实体内容。一般用在只是客户端向服务器发送信息,而服务器不用向客户端返回什么信息的情况。

301 Moved Permanently

永久性重定向,代表资源的链接已经更换了url,在响应报文中会包含新的链接地址。

304 Not Modified

当发出的请求中有附加条件(首部字段有if-*)时,服务器允许访问,但是不满足条件的情况。

400 Bad Request

请求报文内容存在语法错误,服务器处理不了。

401 Unauthorized

发送的请求中含有HTTP认证信息,认证未通过。
返回401的响应必须包含一个适用于被请求资源的WWW-Authenticate首部以质询用户信息

403 Forbidden

请求的资源拒绝被访问,一般是无权限访问。

404 Not Found

这个也很常见,请求的资源服务器找不到。

500 Internal Server Error

服务器在处理请求时,出错了。一般是服务器发生了异常状况。

HTTP首部

首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容。通俗点讲,浏览器和服务器会根据这些字段做出不同的反应,每个字段相当于一条配置信息。
首部字段类似于键值对,请求报文和响应报文都包含首部信息。

这是一段请求首部字段,例如Accept表示浏览器可以接受的响应报文实体类型。

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Host:www.w3.org
If-Modified-Since:Thu, 04 May 2017 23:40:12 GMT
If-None-Match:"a384-54ebb4b41af00;89-3f26bd17a2f00-gzip"

首部字段分为了五种类型,通用首部、请求首部、响应首部、实体首部和拓展首部。

类型 说明
通用首部 是请求和响应都会用到的字段
请求首部 是客户端向服务器发送请求时,报文中包含的首部字段
响应首部 是服务器向浏览器返回响应报文时,包含的首部字段
实体首部 是请求报文和响应报文中针对实体内容的首部字段
拓展首部 是非标准首部字段,由开发者根据自身需求自由定义和实现

以下列表,提供一些首部的简单说明,每个字段的具体使用都不一样,实际应用还是查看详细介绍。

通用首部

字段名 说明
Cache-Control 控制缓存行为
Pragma HTTP/1.0遗留字段,也是用于控制缓存机制
Transfer-Encoding 传输报文主体的编码方式
Trailer 报文主体之后的首部字段,用于分块传输
Upgrade 检测HTTP协议是否可用更高版本
Connection 控制不再转发给代理的字段、连接的管理
Date 创建报文的日期
Via 追踪客户端与服务器之间报文的传输路径,通常指代理服务器
Warning 缓存相关警告

请求首部

字段名 说明
Accept 客户端可接受的媒体类型及相关优先级,q值表示权重
Accept-Charset 客户端可接受的字符集及优先顺序
Accept-Encoding 客户端支持的内容编码及优先顺序
Accept-Language 客户端可处理的自然语言集,以及优先级
Authorization 客户端的认证信息,一般是证书信息
Host 请求资源所在服务器的主机名和端口号
If-Match 比较实体标记
If-Modified-Since 比较资源更新时间
If-None-Match 比较实体标记(与If-Match作用相反)
If-Range 资源未更新时发送实体Byte的范围请求
If-Unmodified-Since 比较资源更新时间(与If-Modified-Since作用相反)
Max-Forwards 最大传输逐跳数(TRACE或OPTIONS方法会用到)
Range 范围请求的实体字节段
Referer 请求页面的原始url
TE 传输编码及优先级
User-Agent 请求客户端的自身信息

响应首部

字段名 说明
Accept-Ranges 服务器是否接受字节范围请求
Age 服务器响应创建经过的时间
ETag 资源配置信息
Location 服务器告知客户端重定向url
Proxy-Authorization 代理服务器向客户端发起的认证信息
Retry-After 服务器告知客户端再次请求的时间
Server 服务器应用名、版本号等相关信息
Vary 代理服务器的缓存管理信息
WWW-Authorization 服务器对客户端的认证信息

实体首部

字段名 说明
Allow 资源支持的请求方法
Content-Encoding 实体内容的编码方式
Content-Language 实体内容的自然语言集
Content-Length 实体内容字节长度
Content-Location 实体内容替代url
Content-MD5 实体内容的报文摘要
Content-Range 实体内容的位置范围
Content-Type 实体内容对应的媒体类型
Expires 实体内容失效日期
Last-Modified 实体内容最后修改日期

Cookie

HTTP协议是无状态的,如果我们今天登录了一个网站,明天重新打开网站时,能自动登录,怎么办?
那就得靠cookie来解决了。它能在客户端保存用户的基本信息,当我们下次访问该网站,浏览器会把cookie一起发送给服务器,服务器就会根据这些信息来判断你是否登陆过。

cookie是网景公司的前雇员卢·蒙特利在1993年3月的发明的。cookie数据也是键值对的形式,它和网站以及网页是关联在一起的。如果保存cookie值指定了网站地址,访问其他网站时并不会发送这些cookie值。

访问一个网站,打开chrome的开发者工具,application中可以看见cookie的信息。

http-3

cookie相关的首部字段

  • Expires:是用来设置cookie的绝对过期时间,默认cookie的生存周期是跟随页面的,页面关闭即失效。
  • Max-Age:是用来设置cookie的相对过期时间,如果同时设置了Expires值,以Max-Age为准。
  • path: 是用来指定与cookie绑定的网页地址,默认情况下,和该网页同一目录下的网页也能访问该cookie。
  • domain: 指定与该cookie绑定的域名,该域名下的网页都可以访问该cookie。
  • secure: 标明传输cookie值的方式。默认情况下,cookie是在不安全的HTTP链接传输,如果设定了该值,cookie将必须处于更安全的方式下才可以传输,比如接下来介绍的HTTPS。

cookie的缺陷

  • 传输:cookie在每一次的HTTP请求中都被附加发送,增加了传输流量。
  • 安全:cookie是明文传输,有安全性问题,会被劫持和篡改。
  • 大小:cookie有大小限制是4kb,更复杂的数据存储是无法满足的。

HTTPS

HTTP协议传输的数据是明文未加密的,为了安全性,网景公司设计了SSL协议(安全套接层),用SSL建立一个安全的通信线路后,就可以在这条线路上进行普通的HTTP通信了,与SSL组合起来就是 HTTPS了,不过现在使用更多的是TLS(安全层传输协议)。

网站使用HTTPS需要申请一个证书,由第三方的可信赖机构提供的,这是为了防止身份伪装。

HTTPS的握手过程

http-4

  1. 客户端发送协议版本,随机码,支持的加密算法和压缩方法
  2. 服务器确认使用的协议版本和加密算法,生成随机码,发送服务器证书
  3. 客户端验证服务器证书,生成48字节key,使用服务器公钥加密key,发送加密信息,客户端握手结束
  4. 服务器将两个随机码+key组合生成本次通信密钥,用来加密信息,服务器握手结束

接下来的通信都将在此加密通信下进行,以上任何确认过程失败,都将断开加密通信。

插曲OpenSSL

看过锤子发布会的,都知道这个组织,但具体干什么的恐怕也没几个人清楚。它是HTTPS中SSL和TLS的具体实现库。
OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包来进行安全通信,其主要库是以C语言所写成,实现了基本的加密功能,实现了SSL与TLS协议。OpenSSL可以运行在绝大多数类Unix操作系统上,OpenVMS与 Microsoft Windows。

未来的HTTP/2

随着互联网的发展,web页面的数据量暴增,服务多年的HTTP/1.1已经显露疲态,由于协议是固定的,所以浏览器,服务器和开发者等都通过各自的手段,来加强服务的响应能力,减缓HTTP/1.1的不足之处。

谷歌开发了SPDY这个实验性协议来提升http的性能,HTTP/2的草案就是基于SPDY3.0展开的。HTTP/2的主要目标是改进传输性能,实现低延迟和高吞吐量。升级HTTP2不会影响原有的网站应用。

二进制分帧

HTTP/2将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码。之前版本的数据传输都是纯文本的方式,二进制的解析更高效,更准确。这是整个HTTP/2性能提升的基础。

多路复用的流

当前问题: HTTP/1.1协议中,一个tcp连接处理一个HTTP请求。例如同时有A和B两个请求,先发送A请求,A请求响应结束,再发送B请求,如果A请求处理的时间很长,B也只能等待,这样就造成了线头堵塞。目前每个浏览器可以同时发起6-8个tcp连接,也依然不能很好地解决大量请求的处理。就算开启了管道机制,一个tcp连接同时发送A和B请求,B还是会等在A之后响应。

HTTP/2的方式: HTTP/2中的请求都在一个tcp连接中处理,每个请求和响应都被分解为独立的帧,然后并行交错发送,再在另一端重新组装。请求之间不再互相干扰,从而消除了不必要的等待和延迟,巨大地提升了性能。

头部压缩

由于HTTP是无状态的,所以每一次请求和响应都会携带头信息,而其中很多头信息是相同的,毫无疑问增加了传输的数据量。

所以HTTP/2设计了专门用于压缩头信息的HPACK算法,在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,并进行更新替换。

服务器推送

当我们访问一个网页时,服务器先返回HTML文档,浏览器再根据这个文档,去请求其中包含的图片,样式表和脚本。

而HTTP/2中,服务器可以对一个客户端发送多个响应,而无需客户端再多起多次请求,客户端可以控制该功能的开启。

参考

  1. http超文本传输协议——维基百科
  2. 蒂姆·伯纳斯·李——万维网之父
  3. 超文本——百度百科
  4. HTTP 协议入门——阮一峰
  5. 99%的人理解错 HTTP 中 GET 与 POST 的区别
  6. 一次完整的HTTP请求与响应涉及了哪些知识?
  7. 《图解HTTP》
  8. 《HTTP权威指南》