在 1989 年三月的一天,CERN(欧洲核子研究组织)的伯纳斯李(Tim BernerLess)博士提出一种让远隔两地的研究者们共享知识的设想。将共享的资料文档相互关联形成超文本(Hyper-Text),文档如何在网络上传输,以及到了另一端如何识别,当时有一个共识这些事情站需要站在另一个巨人 TCP/IP 肩上,形成了一项协议起名为HTTP(超文本传输协议)。

李博士提出设想到 1990 年 11 月第一个网站上线,发展到 1996 年 5 月 HTTP 作为标准公布,并命名为 HTTP/1.0。1997年1月公布了HTTP/1.1,HTTP/1.1还是现在的主流的版本。

HTTP 是建立在 TCP/IP 之上。TCP/IP协议族按照层次分为:应用层、传输层、网络层和数据链路层。应用层决定了向用户提供应用服务时通信的活动,并预存了各类通用的应用服务。比如说 FTP、DNS等,太好了 HTTP 协议就应该在这里。

HTTP 协议在应用层产生数据包(HTTP报文),然后交给 IP协议 把各种数据传给对方。IP 在传输的过程中,不一定可靠,这个时间 TCP 老兄出现了,把传输的数据分割,并采用三次握手的方式准确可靠的传达到对方。访问一个网页一般是用域名,域名怎么和 IP 关联起来呢?这个事情由 HTTP 的另一个兄弟 DNS 负责。

李博士的设想这样变为现实,在 TCP/IP 协议之上的 HTTP 本身,不庞大,但很有内涵,特点十分鲜明。

  • HTTP 协议用于客户端和服务器端之间的通信。这条通信线路上必须有一个客户端和一个服务器,通过请求和响应的交换达成通信。
  • HTTP是一种不保存状态的协议。

众所周知,HTTP是通过 URI 定位互联网的资源。URI 确定后,访问服务器,需要告知服务器客户端意图。如何告知意图呢?HTTP有它的方法,HTTP 方法分别是:

  • GET 获取资源
  • POST 传输实体
  • PUT 传输文件
  • HEAD 获取报文的头部
  • DELETE 删除文件
  • OPTIONS询问自持的方法
  • TRACE 追踪路径,这个方法由于容易引起XST 攻击,已经很少用了
  • CONNECT 要求用隧道协议链接代理。主要使用有 SSL 和 TLS

由于 HTTP 是无状态的,导致每次 HTTP 通信都要断开一次 TCP 链接。当年传输的内容比较少没有问题,随着发展,传输的内容增多,这种方式为通信造成了很大的开销。HTTP/1.1 提出了持久化链接(HTTP keep-live)。持久化链接减少了 TCP 链接的重复建立和断开造成的额外的开销,减轻了服务器的负载。

为了达到持久化,又引入了两个技术,管线化Cookie

管线化之前发送请求后需要等待并接受到响应后,才能发送下一个请求。管线化出现后不用等待响应就可以直接发送下一个请求。

持久化链接时服务端如何知道某个客户端呢?出现 Cookie 做状态管理。Cookie 根据从服务器发送响应报文内的Set-Cookie 的字段信息,通知客户端保存 Cookie。当下次客户端再往服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值发送到服务器。服务器发现客户端发送 Cookie 确定是那个客户端发送过来的请求。

请求和响应的内容是如何组织的呢?

用于 HTTP 协议交互的信息称为 HTTP 报文。请求端的叫请求报文,服务器端的叫响应报文。HTTP 报文本身是由多行(用CR+LF作为换行符)数据构成字符串文本组成。报文又分为报文首部和报文主体两块。

报文中包含请求行包含了请求的方法,请求的 URI 和 HTTP 版本。

状态行包含响应结果的状态码

首部字段包含请求和响应的各种条件和属性的各类首部。

HTTP 首部字段是构成 HTTP 报文的重要要素。通过以 Key 和 Value 形式构成。

通常有 4 种首部:

  1. 通用首部 请求报文和响应报文都会使用到。
  2. 请求首部字段 从客户端向服务端发送请求时使用的首部。
  3. 响应首部字段 从服务器端向客户端返回报文时使用的首部。
  4. 实体首部字段 针对请求报文和响应报文的实体部分使用的字段。

这是请求百度百科产生的报文首部(内容并不全):

1. 1. Request URL:http://baike.baidu.com/
    2. Request Method:GET
   3. Status Code 200 OK
   4. Remote Address:127.0.0.1:9742
       5. Referrer Policy:unsafe-url
 2. Response Headersview source
 3. 1. Connection:keep-alive
2. Content-Encoding:deflate
3. Content-Type:text/html
 4. Date:Mon, 29 May 2017 16:54:28 GMT
5. Server:Apache
6. Tracecode:32681390030198060554053000
7. Transfer-Encoding:chunked
8. Vary:accept-encoding
 4. Request Headersview source
 5. 1. Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
2. Accept-Encoding:gzip, deflate, sdch
3. Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2
4. Cache-Control:no-cache
5. Connection:keep-alive

HTTP/1.1 规范定义了 47 种首部字段,大名鼎鼎的 CookieSet-Cookie 还不在其中。可见首部字段的重要和庞多样化。

内容是怎么的传输呢?

HTTP 传输数据时通过编码提升效率。HTTP协议中有一种被称为内容编码的功能,将数据编码,并保持实体原样进行压缩。常用的内容编码有 gzip、compress、deflate、identity 等。传输大容量数据时,通过把数据分割成多块,能够让浏览器逐步显示页面。这种分块功能称为分块传输编码(Chunked Transfer Coding)。

传输的不同类型的数据,通过 MIME 描述数据类型,需要在 HTTP 报文的首部字段加上 Content-type

传输数据到服务器后,服务器有时间并不能直接知道响应什么最合适。这个时间就用到 HTTP 协议的内容协商机制。客户端和服务器就响应的资源进行交涉,然后提供给客户端最为合适的资源。

内容协商技术有 3 种:

  1. 服务器驱动协商。服务器以请求的首部字段参考,服务端自动处理。
  2. 客户端驱动协商。从浏览器显示可选择列表手动选择。比如浏览器对某个网站提示用英文版之类。
  3. 透明协商。服务器和客户端结合判定的一种方式。

客户端发送数据给服务器端,服务器端需要响应到客户端。如何确定服务器对接受到客户端信息处理是否正常呢?这里引入了HTTP状态码

状态码告知从服务端返回的请求结果。通过状态码,用户知道服务器正常处理了请求还是有其他意外。状态码以 3 位数字组成。数字中第一位指定响应类型,后两位没有分类。

响应类型有 5 类:

  类别 原因短语
1XX Informational(信息性状态码) 接受到的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错

据统计状态码的数量多达 60 多种,实际经常使用的大概 14 种。分别为 200、204(No Content)、206(Partial Content)、301、302、303(See other)、304、307、400、401、403、404、500、503。

目前认识了 HTTP数据包、以及如何传输。同时发现 HTTP 通信是使用明文,而且不验证通信方的身份。不过这事在 1994 年网景开发浏览器时已经意识到,网景在其浏览器中内置对数据进行加密和解密操作,并返回网络上传送回的结果。这个加密层称为安全套接(SSL) ,以 HTTP 应用层的子层出现,最初 HTTP 和 SSL 一起使用的,在SSL 逐渐演变到 TLS 时,最新的 HTTPS 在 2000 年五月公布的RFC 2818 正式确定。

HTTPS=HTTP+加密+认证+完整性保护

有了 HTTPS 后安全性的问题得到一定的解决。如果一个网站,某些内容只要让特定人群访问,该如何做呢?

HTTP/1.1 确定了4 种重要的认证方式

  1. BASIC 认证
  2. DIGEST 认证
  3. SSL 客户端认证
  4. FormBase 认证(基于表单的认证)

这些问题解决后,似乎 HTTP 协议保证我们在互联网上自由的冲浪了。97 年的颁布的 HTTP/1.1 的确在这么多年基本满足我们的需求。随着 Web 技术的进一步发展,HTTP/1.1 在好多方面捉襟见肘。这期间出现好多解决方案,比如 Google 的 SPDY。终于等到2015年出现了 HTTP/2

接下来,我去认识 HTTP/2 去了!