http端口_HTTP(1):最熟悉的陌生人

d1c212379669d46381e6111ba19f21f9.png

6HTTP

经过了长途跋涉,我们终于从网络层来到了应用层。按照 OSI 七层模型,传输层之上还有会话层、表示层、应用层等三层,按照 TCP/IP 五层模型,传输层之上就只有一层应用层。不过无论怎么划分,应用层都包含很多协议。由于篇幅和主题的原因,本文只能挑选其中其中一个典型代表 HTTP 来讲述,如图6-1所示: 

726ed457702c700304ac924669935b1c.png

图6-1 HTTP

HTTP(Hypertext Transfer Protocol,超文本传输协议),从某种意义上来说,也许是我们最熟悉的陌生人。它无处不在,不过在很多时候,我们都没有意识到 HTTP 的默默奉献。

我的文字里有淡淡的芬芳

那是因为散发着你为我磨的墨香

我泼出了大地的苍茫

撒进了明月的柔光

我们上网冲浪时,流连于各个丰富多彩的网页,而 HTTP 就是那个为网页磨墨的人。

HTTP 有多种版本:HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2,本文选择迄今为止使用最广泛、最流行的 HTTP/1.1 版本进行讲述。如无特别说明,本文中的 HTTP 中的都是 HTTP/1.1。

要介绍 HTTP,有很多维度:从编程的视角、从内核的视角、从安全的视角、从架构的视角......本文还是一以贯之,从网络协议的视角,来介绍 HTTP。

说明:

1、现在上网一般是使用 HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),但是 HTTPS 并不是抛弃 HTTP,反而它仍然是依赖 HTTP。所以掌握 HTTP,仍然有其足够的必要性和价值。关于 HTTPS 与 HTTP 的关系,下文会有介绍。

2、HTTP(及 HTTPS),其应用范围绝对不止“上网”这一个场景,只不过“上网”比较典型而已。

6.1 HTTP 综述

与其他应用层协议一样,HTTP 有天然的使命,那就是网络协议数据的源头(之一)。为什么这么说呢?我们先看看 HTTP 与下层协议之间的关系,如图6-2所示: 

3d9e4e3e3131bb271940f24e98930236.png

图6-2 HTTP 与下层协议之间的关系

很多资料,都有类似图6-2的描述。HTTP Client 为了从 HTTP Server 获取某些信息,向 HTTP Server 发送了1个 HTTP Request。从网络协议的角度来说,这个 Request 先是从 Client 端的 HTTP 协议栈到达自己的 TCP、IP ...... 然后到达 Server 端的 IP、TCP,最后抵达 HTTP Server。在 Client 端,是报文头的层层封装,在 Server 端,是报文头的层层解封。

我们暂时不关心 HTTP 的报文格式(下文会讲述),只关注这一条数据流的源头。可以看到,它的源头正是 HTTP Client。对于 TCP、IP 来说,它们并不关心数据的来源,反正有“人”喂给它。或者说,TCP、IP 不产生数据,它们只是数据的搬运工。产生的数据是 HTTP Client。

同理,如果 HTTP Sever 回应1个 HTTP Response,那么产生数据的就是 HTTP Server。

也正是从这个角度来讲,HTTP(及其他应用层协议)是全村的希望,是网络协议的数据源头。 

d972ef033e9f4528726ba706fe7934a7.png

当然,如果抛开网络协议,继续追根溯源,产生数据的就不是 HTTP,而是人。而人,产生数据的源头,则是性。HTTP 没有爱情,但是苍老师赋予了它的生命。

8f8f61cc817fb818346feb7ad4a5280b.png

话题扯远了,我们还是从网络架构入手,对 HTTP 做一个基本了解吧。

6.1.1 HTTP 基本网络架构

HTTP 是众多应用层中的一个协议,它的下层协议是传输层中的 TCP。而且与 TCP 不同的是,HTTP 是一个典型的 C-S(Client-Server)架构协议,如图6-3所示:

925344798c5ef789c9b2ed09877618d5.png

图6-3 HTTP Client-Server

从报文的角度,C-S 架构的典型特征是 Request-Response(请求-响应)模式。HTTP Client 发送1个 Request 报文,而 HTTP Server 回应1个 Response 报文。

需要说明的是,所谓 C-S 架构,其实是一种角色扮演。从来没有规定哪一个计算机、哪一个程序/进程,一定只能是 HTTP Server 或者只能是 HTTP Client。一个机器上的一个进程,完全可以同时扮演 HTTP Client 和 HTTP Server。 

a00595afe9eaa1a324d16adfb84f2916.png

人生如戏,全靠演技

扮演一个 Client 或者 Server,其背后总要实现一定的程序——这一点我们在后面介绍 HTTP 具体协议内容时再详细讲述,这里只从“端口号”这一特征来描述两者的不同。

端口号,就是 TCP 报文头中的 Source Port(源端口)、Destination Port(目的端口)。对于 HTTP Server 来说,它的端口号是相对固定的,而 HTTP Client,其端口号则不需要固定,可以变化(我们称之为临时端口号)。

要访问一个 HTTP Server,除了知道它的 IP 地址(或者域名),还得知道它的端口号(关于端口号的概念,可以参阅 5.1.1 源端口号/目的端口号(Source Port/Destination Port)),所以 HTTP Server 的端口号需要相对长期的固定。而且,HTTP Server 一般采用 ICANN 所分配的周知口 80,这对于一个 Web Server 来说尤其重要,不然的话,谁会知道你的端口号是啥呢?

但是,对于 HTTP Client 来说,它的端口号则不需要固定。当访问 HTTP Server 时,HTTP Client 可以临时绑定一个端口号,访问结束以后,可以释放这个端口号。当下次再访问时,HTTP 可以再临时绑定另一个端口号即可。当然,HTTP Client 也可以长期绑定一个端口号(不释放),这也没关系。

HTTP 也有连接的概念,它的本质或者说前提,是 TCP 连接。HTTP Client 在发送 Request 报文之前,需要先与 HTTP Server 建立1个 TCP 连接。对于 HTTP/0.9 来说,当 回应1个 Response 报文以后,HTTP Server 就会断开这个连接。HTTP/1.0 也是如此,只不过使用了一个规避手段,使这个连接变成长连接。HTTP/1.1 正式支持长连接特性,连接何时释放,由 HTTP Client/Server 自己决定。

基于 TCP,C-S 架构,其中,Client 只需要临时绑定一个端口号,而 Server 则需要一个相对固定的端口号(一般是周知口80),这样的话,HTTP Client/Sever 之间,就可以传递数据了。那么 HTTP 所传递的是什么数据呢?

对于物理层、数据链路层(以太)、网络层(IP)、传输层(TCP)来说,它们并不在乎自己所传输的数据是什么格式,是一个超文本?是一个图像?是一组火星文?没关系,是什么都行,它们既不在乎,也不会去关心。

但是 HTTP 则不然。从网络协议来讲,HTTP 这一层协议如果不关心,就没有协议关心了,因为它(们)是最高层协议,再上面就没有了。物理层关心光和电,HTTP(应用层)则需要关心粮食和蔬菜。 

7d9bfd3f8b46b62ca2b9588a24123992.png

面朝大海 春暖花开

HTTP 的粮食和蔬菜,是不是正如其名称所暗示的那样,就是传输超文本呢(Hypertext)?不一定!

这么说,可能越来越解释不清楚。让我们暂时忘记超文本,先来看看 HTTP 的报文格式吧。

说明:更严格地说,基于浏览器上网的场景,是一种 B-S 架构(Browser-Server,浏览器-服务器)。B-S 架构是对C/S结构的一种变化或者改进的架构。在这种结构下,用户界面完全通过WWW浏览器实现。但是,正如前文所说的,HTTP 的应用不止上网一个场景,所以我们还是将 HTTP 当作是一种 C-S 架构协议。

6.1.2 HTTP 的报文格式简述

如果是第一次接触 HTTP 的报文格式,可能会有两个不习惯,甚至会懵圈。

第1个不习惯是 HTTP 对于报文中各个字段的定义。其他协议定义各个字段,都是画一个图,指明各个字段的长度(占位多少比特),比如图6-4就是 TCP 报文格式的表达方法: 

d5b7351ba9e3cd620ff2c01fdd656cce.png

图6-4 TCP 的报文格式

图6-4这样的表达,就显得很专业,很有逼格,而 HTTP 的表达方式就是一堆字符串(下文会讲述这个字符串),显得比较普通。就像有的童鞋知道了“一骑红尘妃子笑”中的“骑”跟“骑马”中的“骑”是一个读音的时候,感觉这么多年的学都白上了。 

8316a7ba5011f4c07e3c5ebfcba66f3c.png

这只是一个调侃,但是当看到 HTTP 报文格式表达的第2个特点时,可能真要哭了。HTTP 采用 ABNF(Augmented Backus-Naur Form,扩展的巴科斯-诺尔范式)来表达它的语法。 

6fc253913603df5dddda54480e47bac7.png

由于主题和篇幅的原因(主要是鄙人也不懂),本文不会去介绍 ABNF(如果感兴趣,您可以参考 https://tools.ietf.org/html/rfc5234)。不过如果仅仅是为了看懂 HTTP 的语法,也不需要专门学习 ABNF,很多 ABNF 也是一看就懂。

下面我们就来看一个 HTTP 报文的例子(摘自 https://tools.ietf.org/html/rfc7230#page-34,笔者做了删减):

Client request:

     GET /hello.txt HTTP/1.1

     User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3

     Host: www.example.com

     Accept-Language: en, mi

Server response:

     HTTP/1.1 200 OK

     Date: Mon, 27 Jul 2009 12:28:53 GMT

     Content-Type: text/plain

     Hello World!

这是 HTTP Client 与 HTTP Server 一来一回(Request/Response)的报文,为了易于讲述和理解,笔者做了删减。从表面上看,这确实就是一堆字符串,虽然文字本身能够看懂,但是这些文字很难一下子与报文格式挂钩。

其实,HTTP 的报文格式,与 TCP、IP 等协议的报文格式,其本质是一样的,都是分为两部分:报文头(PHU(Protocol Header Unit,协议头部单元)) + 报文数据(PDU(Protocol Data Unit,协议数据单元)),只不过是关于协议中字段的定界的方法不同而已。

TCP、IP等协议所采取的定界方法是“字段起始位置”——从哪个 bit 开始,到哪个 bit 结束,这一串 bits 代表一个字段,比如 TCP Header,从第0个 bit,到第15个 bit,这16个 bits 代表 Source Port 这个字段。

而 HTTP 是用一些分隔字符等语法来定界字段,如图6-5所示:

64422b0ea9ba287793f3c038751726cd.png

图6-5 HTTP 报文格式示意

图6-5是将前面例子中 HTTP Server 的 Response 报文做了分析,可以看到,整个报文分为三大部分:

(1)HTTP Header,包括图中的start-line、header fields 等内容。

(2)HTTP Header 与 HTTP Data 之间的分隔符。用大白话说,这个分隔符就是一个空白行,用专业术语说,这个分隔符就是回车(CR, carriage return)和换行(LF,line feed)两个字符(对应的 ASCII 码分别是:CR = 0x0D,LF = 0x0A)。

(3)HTTP Data,就是图中的 message-body 部分。

HTTP Request 与 HTTP Response 报文的格式是一样的,如果用 ABNF 来表达的话,就是:

HTTP-message   = start-line

              *( header-field CRLF )

              CRLF

              [ message-body ]

下面我们就简单介绍一下这个报文格式(详细的介绍在后面的章节),不过在介绍之前,我们先介绍几个知识小点。

6.1.2.1 几个知识小点

第1个知识小点就是回车和换行。抛开 HTTP 不谈,回车和换行在计算机的世界里,也是出镜率非常高的两个词汇。

如果用 windows 自带的“记事本”编辑文档时,当我们敲击键盘上的回车键,鼠标的光标会移到下一行的开头的位置。 

842cfc24abef82841030f7bf42f21cfc.png

这个行为的设计,是源于早先的机械式英文打字机。在机械英文打字机上,有一个部件叫“字车”,每打一个字符,“字车”就前进一格。当打满一行字符后,打字者就得推动“字车”到起始位置,这时打字机会有两个动作响应:一是“字车”被归位,二是滚筒上卷一行,以便开始输入下一行,这个推动“字车”的动作叫“回车”。 

80db9a20c1396edcf07062dc7be6b38e.png

后来,在电动英文打字机上,人们增加了一个直接起“回车”作用的键。这个新增的键就被称为“回车键”。 

15504ccf98da0a64b90b6023f3a20382.png

在随后的电脑键盘上,“回车键”上曾经使用过“CR(carriage return)”、“RETURN”的字样,后来才统一确定为“Enter”。 

62847d5f6d8df8ddeb6804edb6f459a3.png

不过不管电脑键盘上的回车键叫什么名字,敲一下回车键,我们习以为常的行为(移到下一行的开头位置)其背后本质仍然是两个动作:

(1)回车:回到改行的开头位置

(2)换行:换到下一行

所以在 ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)码中,分别有两个值与之对应:CR = 0x0D,LF = 0x0A。

总之,日常口语中,我们所说的回车和换行,其对应到 ASCII 码可能是模糊的,可能只对应其中一个字符、也可能对应两个字符(CR + LF)。而在 HTTP 的世界里,我们一定要精确地表达我们所说的字符:回车字符就是回车字符(CR),换行字符就是换行字符(LF),回车换行就是回车加换行两个字符(CRLF)。

为什么要强调精确表达呢?这就涉及到了第2个知识小点:CRLF 是 HTTP 最最最重要的分隔符。虽然只是两个简单的字符,但是 CRLF却是分隔 HTTP 各个字段的“达芬奇密码”,可以对 HTTP 进行庖丁解牛——如果说人话,那就是:可以简单认为 HTTP 的一行字符串,就代表一个“字段”。

本小节要介绍的第3个知识小点是:用 ABNF 所表达的 HTTP 报文结构,你所看到的空格和换行都不是真正的空格和换行,空格和换行一定要有专门的字符来表达,空格用 SP 表达(SP,single space,对应的 ASCII 码是 0x20),换行用 CRLF 表达(是 CR、LF 两个字符),如图6-6所示: 

5c9e6877c6209331c13bfd18a50f6857.png

图6-6 视觉上的空格和换行

图6-6中,我们将 HTTP-message 的语法表达中的视觉上的空格、换行用符号特别标示出来。但是这些标示出的空格和换行,并不是说 HTTP-message 中要包含这些空格和换行,这只是为了方便人们的阅读而产生的字符。HTTP-message 中如果要包含空格和换行,它一定要用明确的字符表达。

这么说,可能仍然显得苦涩和拗口,佶屈聱牙,那就让我们看看 HTTP 的具体的报文格式,边看边体会吧。

6.1.2.2 start-line

start-line,正如其名字所暗示的,是 HTTP 报文的第1行,按照 TCP、IP 等协议的说法,那就是第1个字段。不过与 TCP 等协议不同的是,HTTP 协议并不是以字段长度来定位字段的结尾,而是以分隔符 CRLF 来定位 start-line 的结尾。换句话说,HTTP 协议本身并不限制 start-line 的长度:start-line 愿意多长就多长,直到遇到 CRLF。

HTTP Request 与  HTTP Response,两者关于 start-line 的具体定义是不同的,实际上这也是两者唯一定义不同的字段。也就是说,要想从语法上区分一个报文到底是 HTTP Request,还是 HTTP Response,也只能通过这个字段。

用 ABNF 表达,start-line 的定义是:

start-line = request-line / status-line

符号“/”是或者的意思,此定义的意思是:start-line 或者是 request-line,或者是 status-line

1)request-line

request-line 是 HTTP Request 的 start-line,其更具体的定义是:

request-line = method SP request-target SP HTTP-version CRLF

这里我们看到,关于空格(SP)、回车换行(CRLF),ABNF 都明确地标识出来,而定义中视觉上的空格,则仅仅是为了阅读方便,不然的话,如果将视觉上的空格去掉,则难以阅读:(其实,视觉上的空格,是英语语法的单词分隔符)

request-line=methodSPrequest-targetSPHTTP-versionCRLF

下面我们继续解释 request-line 定义中的每一个“单词”。

  • method

既然是请求(Request)报文,那就是请求对方(HTTP Server)做一些事情,这跟现实生活中请求别人帮忙是一样的。 

3fc6eb86d9c27c0d1f2a6a11da112f8b.png

只是 HTTP 的请求没有那么“客气”,而是直奔主题,简单地说就是期望对 HTTP Server 上的信息做一些动作(method),这些动作包括:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE,一共8个 method。

这8个 method,我们会在后面的章节中详细描述,这里只是简单介绍一下 GET、DELETE,以有一个直观认识。

GET 就是查询(检索)HTTP Server 上的相关信息,DELETE 就是删除 HTTP Server 上的相关信息。

  • request-target

无论是 GET,还是 DELETE,或者是其他 method,都需要指明具体的信息定义。比如 GET,总得告诉 HTTP Server,你需要查询什么样的信息。就像生活中很多动词后面总得接一个名词,不然别人听不懂你到底要干什么? 

167196d2f3c2b5718e02bc6b2d8fbefc.png

不是每个动词后面,都可以不接名词

这个“名词”就是 request-target。当然,HTTP 的 request-target,可能不止一个单词,它要复杂许多:

request-target = origin-form

                 / absolute-form

                 / authority-form

                 / asterisk-form

这样表达,可能就不是“复杂许多”,而是让人有点凌乱了。更详细的讲述我们放到后面的章节,这里我们只须简单理解 request-target 就是 URL(Uniform Resource Locator,统一资源定位符),或者就理解为我们上网时所敲的网址,比如:https://www.baidu.com/。

至此,我们可以看到 HTTP Request 报文的 request-line 的最核心要表达的内容就是请对方(HTTP Server)“do sth.”,也就是:

method SP request-target

  • HTTP-version

HTTP-version 比较简单而直接,就是表达当前 HTTP Client 所使用的 HTTP 的版本号。HTTP 的版本有 HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2。在 request-line 里带上版本号,是出于兼容性的考虑。现在主流使用的 HTTP-version 是 HTTP/1.1。

  • SP 和 CRLF

在 request-line 的定义里,出现了 SP 和 CRLF:

request-line = method SP request-target SP HTTP-version CRLF

现在我们再结合前文说的内容,就会比较清楚:

(1)method 和 request-target 之间需要一个空格分隔,所以reqeust-line 会在两者之间,明确地加上一个字符“SP”来标示。同理,request-target SP HTTP-version,也是如此。

(2)request-line 需要1个结尾符,同时 HTTP 也需要在不同的字段之间有个分隔符,而这个分隔符就是“CRLF”,所以 request-line 的最后明确地写有“CRLF”。

另外,我们也可以总结出:CRLF 是大字段之间的分隔符;SP 是大字段内部小字段的分隔符。像 request-line 就是大字段,method、request-target、HTTP-version 等就是大字段内部的小字段。

2)status-line

status-line 是 HTTP Response 报文的 start-line,其定义如下:

status-line = HTTP-version SP status-code SP reason-phrase CRLF

其中,HTTP-version 的定义与 request-line 中的 HTTP-version 的定义相同,它表达的是 HTTP Server 所使用的版本。现在主流使用的版本是 HTTP/1.1。

  • status-code

status-code 标识 HTTP Server 对 HTTP Client 的请求(Request)的响应结果,由3位数字组成。最著名的 status-code 也许就是“404”——表示:所请求的页面不存在或已被删除(RFC 7231 对 404 的官方定义是 Not Found:The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists.)。

4c6c2489149c25d30f53ce4b982fd1ca.png

status-code 的 ABNF 语法格式为:

status-code    = 3DIGIT

其中,DIGIT 代表十进制的数字0~9,3代表有3个 DIGIT。

status-code 的3位数字中的第1位,代表 status-code 的类别,其余两位数字代表更详细的含义。比如以4开头的 status-code 4xx 代表客户端错误(Client Error):4xx (Client Error): The request contains bad syntax or cannot be fulfilled,而更具体的 404 代表 Not Found,414 代表request-target 太长,服务器无法解释(The 414 (URI Too Long) status code indicates that the server is refusing to service the request because the request-target is longer than the server is willing to interpret)。

HTTP/1.1 一共定义了5类 status-code,分别是:

1xx:信息,服务器收到请求,需要请求者继续执行操作(Informational: The request was received, continuing process)

2xx 成功,操作被成功接收并处理(Successful: The request was successfully received,understood, and accepted)

3xx 重定向,需要进一步的操作以完成请求(Redirection: Further action needs to be taken in order to complete the request)

4xx 客户端错误,请求包含语法错误或无法完成请求(Client Error: The request contains bad syntax or cannot be fulfilled)

5xx 服务器错误,服务器在处理请求的过程中发生了错误(Server Error: The server failed to fulfill an apparently valid request)

更详细的信息,请您参考 RFC 7231,我们也会在后面的章节中,做进一步的描述。

  • reason-phrase

status-code 是 HTTP Server 对 HTTP Client 的请求(Request)的响应结果的数字表达,而 reason-phrase 则是一种文字表达(可以理解为字符串)。简单地说,status-code 是为了机器的理解(HTTP Client 程序需要解析并理解 status-code),reason-phrase 是为了人的理解。

早期的文字交互的 HTTP Client 程序,会将 reason-phrase 显示给用户看,现在的各种浏览器等已经不需要这些内容。HTTP/1.1 保留此字段,更多是处于兼容性的考虑。对于 HTTP Client 来说,它可以忽略这个字段。

6.1.2.3 Header Fields

类比 TCP、IP 等协议的报文结构,HTTP 的报文头包括:start-line 和 Header Fields。所以,关于 Header Fields,第一个要澄清的概念就是:它是 HTTP 报文头的一部分,但不是报文头的全部。从清晰、不引起混淆的这个角度来讲,Header Fields 这个命名有点瑕疵,它有点给人以“Header Fields 就是 HTTP Header 的全部”的误解。也许将其命名为 Header Attribute Fields 更好。

不过还是那句话,无论对错,这样的命名已经事实了,我们也无法改变。我们能做的只能是:接受这样的命名,但是心里清楚是怎么回事。

说明:再次强调一遍,Header Fields 并不区分 HTTP Request 和 HTTP Response 的,两者的 Header Fields 的定义是相同的。

Header Fields 并不是一个字段,而是多个字段,用 ABNF 的语法表示,就是:

Header Fields = *( header-field CRLF )

其中,星号(*)代表0到多个的意思,也就是说 Header Fields 可以包含0个或多个 header-field,每个 header-field 后面要加上 CRLF 分隔符——更直白地讲,就是:一行一个 header-field。

header-field 是 HTTP 头部真正的字段,它的定义如下:

header-field   = field-name ":" OWS field-value OWS

其中,OWS 是 optional whitespace 的意思,也就是说这些(可以是多个)空白字符(空格,TAB)是可选的。我们看一个 header-field 的具体例子:

Content-Type: text/html

其中,Content-Type 就是 field 的名字(字段名),text/html 就是这个字段的值(字段值),字段名和字段值之间用冒号(:)分隔,冒号的后面和字段值的后面,可以加上多个空白字符(可选的)。

关于 header-field 更详细的定义,我们放到后面的章节讲述。这里只是稍微再介绍一下 Content-Type,以对 header-field 有个直观的理解。

我们知道,HTTP 的报文包括 Header 和 Data 两部分(Data 是可选的),而 Content-Type 就是 Data 的数据类型。数据类型的表达方式是:type/subtype(还包括可选的参数,我们先忽略之),比如 text/html 指的就是:数据主类型是 text(文本),子类型是 html。

数据类型是告诉 HTTP Client 或者 Server 该如何解析 HTTP 报文中的数据。这跟我们计算机中的文件类型(文件的后缀)的目的是一样的,比如看到 .docx 文件,计算机就会用 word 或者 office 等文字处理软件打开,看到 .png 文件,就会用 mspaint(画笔)等图像处理软件打开。

HTTP 的 Content-Type 有很多种:text/html,text/xml,image/gif,image/png,video/avi,application/x-javascript,application/json ......

通过 Content-Type 我们可以总结两点:

(1)Header Fields(包含多个(header-field))是对 start-line 的补充,毕竟 start-line 所包含的内容是有限的,绝大部分场景下的 HTTP,还需要 Header Fields 来补充信息,才能完成有效的通信。

(2)呼应前文的问题“HTTP 所传输的数据,是不是正如其名称所暗示的那样,就是传输超文本呢(Hypertext)?”,现在来看,答案已经很明显了:理论上说,HTTP 可以传输任何你想要传输的内容,绝不仅仅局限于超文本。

6.1.2.4 Header 与 Data 之间的分隔符

从视觉效果上来讲,HTTP 的报文头(Header)和数据(Data)之间的分隔符是一个空行,从字符的角度来讲,就是这一行里只有 CR、LF 两个字符,再也没有其他字符。

我们再看一遍 HTTP 的报文格式,就能理解 HTTP 为什么要这么做,如图6-7所示:

6731bb3bdbfbc15ca391249ab9941fce.png

图6-7 HTTP 报文格式示意(2)

从图6-7可以看到,HTTP 的报文格式其实就是一行行字符串,所谓:

规范千万条

分隔第一条

字段不换行

解析泪两行

HTTP 之所以能够区分 start-line 和 header-field,是因为 HTTP 的语法规定 start-line 就在第1行,而且只有1行,所以 HTTP 可以简单认为从第2行开始,就是 header-field。

但是 HTTP 同时规定,header-field 可以是0行,也可是多行(但是又没有限制到底是多少行),那么 HTTP 该如何区分 header-field 和 Data(HTTP 的数据)呢?

HTTP 选择的方案是:使用空白行(CRLF)。在这个空白行之前,就是 HTTP Header,在这个空白行之后,就是 HTTP Data。如此一来,HTTP 就可以轻易地区分 header-field 和 Data。 

ba96a0b0c54fada9d8f480a9e5e62b76.png

我们知道,HTTP 的数据是可选的,也就是说 HTTP 的报文里可能不包含数据部分。那么,如果没有数据,HTTP 是否还需要那个空白行呢?答案是 YES!

即使没有数据部分,HTTP 也需要一个空白行,因为这个空白行同时还是标识 HTTP Header 结束的标志。即使没有数据,HTTP 也要知道 HTTP Header 已经结束了。

当然,您也可以说,报文结束了,如果没有数据的话,那也就意味着 Header 结束了,为什么还要一个空白行来标识呢?这不是不可以,不过从无歧义的角度来讲,加1个空白行(CRLF),更加明确。否则的话,到底是 Header 结束了,还是漏写了内容?这无从分晓。

所以,HTTP 规范规定,无论是否有数据,HTTP 报文头部,都是以1个空白行(CRLF) 结尾。

6.1.2.5 message-body

message-body 就是 HTTP 报文结构中的数据部分,它是可选的,毕竟不是每个 HTTP Request/Response 都需要数据部分。message-body 的语法是:

message-body = *OCTET

其中,OCTET 指的是任意8比特数据(any 8-bit sequence of data),星号(*)代表0到多个的意思,这也指明了 message-body 是可选的(如果个数是0的话,就意味着没有 message-body)。

如果仅仅是传输,那么 HTTP 完全可以把 message-body 当作黑盒,就跟 TCP、IP 协议一样,不必在意和关心 message-body 的数据格式。但是前文说过,HTTP 必须关心它的粮食和蔬菜,而指明 message-body 数据格式的,就是 HTTP 报文头中的 Content-Type 字段。

实际上,就算只考虑传输,HTTP 也没有完全把 message-body 当作黑盒。想象这样的场景,copy 一个文件,如果这个文件比较大的话,我们一般会将这个文件先压缩一下。人如此,HTTP 也会如此。所以 HTTP 报文头中有个字段 Transfer-Encoding 标识 message-body 的传输编码方式,比如 Transfer-Encoding: gzip,表示 message-body 采用了 gzip 的压缩。

通过以上描述可以看到,从学习 HTTP 的角度而言,要理解 message-body,更多的是要理解 header-field。这里我们暂时就介绍这么多,能对 message-body 有个直观的了解即可,更多的内容,我们放到后面的章节讲述

6.1.2.6 小结

HTTP Request 和 HTTP Response 的报文格式是一样的,其语法格式为:

HTTP-message   = start-line

                 *( header-field CRLF )

                 CRLF

                 [ message-body ]

虽然这种用 ABNF 语法描述的报文格式,与传统的 TCP、IP 等协议的报文格式的表达方式不一样,但是其本质都是一样的,包括:报文头(Header)和报文数据(PDU)。

而且,归根结底,报文头都是由各个字段组成的,只不过传统的 TCP、IP 等协议,使用字段长度(字段所占有的比特位)来分隔字段,而 HTTP 是使用 CRLF(回车换行)来分隔字段。

HTTP 的报文头由1个(行)start-line、0到多个(行) header-field 组成,这些字段字段之间的分隔符就是 CRLF。

HTTP Request 与 HTTP Response,两者的报文,从语法的角度来讲,仅仅是 start-line 的细节不同。

HTTP Request 的 start-line 取名为 request-line,其语法格式为:

request-line = method SP request-target SP HTTP-version CRLF

request-line 主要表达 HTTP Client 对 HTTP Server 的诉求:想要对什么资源(request-target)做什么样的动作(method)

HTTP Response 的 start-line 取名为 status-line,其语法格式为:

status-line = HTTP-version SP status-code SP reason-phrase CRLF

status-line 主要表达 HTTP Server 对 HTTP Client 诉求的响应的结果(status-code)。

无论是 Request 还是 Response,如果还要表达更多的细节内容,则需要借助 header-field。header-field 的语法格式为:

header-field   = field-name ":" OWS field-value OWS。

HTTP Header 以一个空行(只有 CRLF,而没有其他字符)结尾。这个空行既是 HTTP Header 结束的标志,也是 HTTP Header 与 HTTP 数据(message-body)的分隔符。

message-body 是可选的,因为不是每个 HTTP 报文都需要 message-body。message-body 由0到多个 OCTET 组成。

与 TCP、IP 等协议不同,HTTP 没有将 message-body 当作黑盒,而是通过报文头中的 Content-Type、Transfer-Encoding 等 header-field 来表达 message-body 的数据格式、传输编码等信息。

以上只是对 HTTP 的报文格式的泛泛而谈,我们会在后面的章节中,继续详细描述具体的内容。

6.1.3 HTTP 历史简述

北京时间2012年7月28日伦敦奥运会开幕,开幕式第六章的名字是“弗兰克和琼恩说,感谢蒂姆”。弗兰克是个男孩,琼恩是个女孩。他们要感谢的蒂姆就是Timothy John Berners-Lee——是的,就是那个于1989年发明了 WWW 的蒂姆·伯纳斯·李。 

7ae5ae4e50355b7f3ed8b2fe4f02f5ba.png

WWW 的基本技术基石之一就是 HTTP。自伯纳斯·李发明一来,HTTP 先后经历了 HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2 等4个版本,如表6-1所示:

表6-1  HTTP 版本历史

HTTP

版本

对应 RFC

RFC

发布时间

备注

HTTP/0.9

——

——

发布于1991年,没有对应的 RFC

HTTP/1.0

RFC 1945

1996.05

HTTP/1.1

RFC 2068

1997.01

现在已经被 RFC 7230, 7231, 7232, 7233, 7234, 7235 所取代

HTTP/2

RFC 7540

2015.05

下面我们就分别简要描述一下这几个版本。

说明:

1、2018年10月28日,IETF HTTP和QUIC工作组主席马克·诺丁汉在邮件列表讨论中提出正式请求,将HTTP-over-QUIC重命名为HTTP/3,以“明确指出它是HTTP语义与有线协议的另一种绑定……因此,人们理解它与QUIC的分离”,并在完成和发布草案之后,将其开发从QUIC工作组传递到HTTP工作组。在随后几天的讨论中,诺丁汉的提议被IETF成员接受,他们在2018年11月正式批准HTTP-over-quic成为HTTP/3。

2、QUIC(读作“quick”)是一个实验性的传输层网络协议,最初由谷歌在2012年设计、实现和部署,并在2012年随着实验范围的扩大而公开宣布

3、由于篇幅和主题的关系,本文不涉及 HTTP/3 的内容

6.1.3.1 HTTP/0.9

1991年发布的 HTTP/0.9 并没有对应的 RFC,其文档可以参见 https://www.w3.org/Protocols/HTTP/AsImplemented.html。大概那时候 HTTP 还没有进入 IETF 的法眼吧,毕竟就在前一年(1990年9月份),伯纳斯·李去去参加欧洲超文本技术大会的时候,这个大会还根本不知道伯纳斯·李在说什么。(IETF,The Internet Engineering Task Force,国际互联网工程任务组,RFC 就是由其定义和发布) 

73f45276250f956c56eebf01954f4e5e.png

HTTP/0.9

HTTP/0.9 比较简单,我们看一个示例:

#1、创建 TCP 连接

Connected to xxx.xxx.xxx.xxx 80

#2、HTTP Client 发送 HTTP Request

GET /index.html

#3、HTTP Server 的 Response(HTML)

  Hello World

#4、关闭 TCP 连接

这既是一个示例,也基本描述了 HTTP/0.9 的全部特性:

(1)基于 TCP 连接,1个 HTTP Request-Response 需要1个 TCP 连接。即每次 HTTP Client 的 Request 之前需要先建立 TCP 连接,而 HTTP Server 回应1个 Response 以后,这个 TCP 连接就关闭了

(2)只支持1个 method:GET

(3)只支持 text/html 数据类型(这也是 HTTP 之所以称为 HTTP 的原因)

(4)不支持 Header Fields。我们可以想象一下 Request 报文的样子,就只是“GET ***”之类的形式了,所以 HTTP/0.9 也被称为:The One-Line Protocol

显然 HTTP/0.9 显得有点过于简单,但这并妨碍她的伟大,因为一个新的生命诞生了,这个生命终将点亮全世界! 

bcb0d29cc438ac9189ad4610e1498f1b.png

6.1.3.2 HTTP/1.0

在独立完成 HTTP/0.9 之后,伯纳斯·李并没有停下脚本,这一点可以在 https://www.w3.org/Protocols/HTTP/HTTP2.html 文档中一件端倪。(HTTP2.html 不是 HTTP/2) 

b5b03cdfa137bb94f011e4796c0490d4.png

1992年发表的 Basic HTTP 并没有正式的版本号,我们姑且称之为 HTTP/0.92 吧(这只是笔者的一个杜撰,请您不要受误导)。也许 HTTP/0.92 更正式的称呼应该是 Internet Draft,正如 https://www.w3.org/Protocols/HTTP/HTTP2.html 所描述的那样:

This document is a DRAFT specification of a protocol in use on the internet and to be proposed as an Internet standard ......Internet Drafts are working documents of the Internet Engineering Task Force (IETF) ......

不过,我们还是称之为 HTTP/0.92 吧,因为 HTTP/1.0(RFC 1945)的版本历史是从1994年开始的,并没有包含这段岁月(下文会描述)。

如果说 HTTP/0.9 只是一个雏形,那么 HTTP/0.92 就像是 HTTP/1.0、HTTP/1.1 的背影。是的,只要看到美丽的背影,就能知道那是心中的她。

2d7d9f0862e47694f1e4d0f271047663.png

HTTP/0.92 已经完整地定义了 HTTP/1.0、HTTP/1.1 的报文结构:

HTTP-message   = start-line

                 *( header-field CRLF )

                 CRLF

                 [ message-body ]

只不过,在 header-field、method 层面还有待完善。比如,method,HTTP/0.92 所定义的有:GETHEAD、CHECKOUT、SHOWMETHOD、PUTDELETEPOST、LINK、UNLINK、CHECKIN、TEXTSEARCH、SPACEJUMP、SEARCH。这与 HTTP/1.1 的8个 method(GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE)还是有一定的出入(不过已经很完美了)。

我们没有必要在技术层面停留在1992年的 HTTP/0.92,还是让时钟拨到1994年11月。从 RFC 1945(HTTP/1.0 正式版本)所述的历史来看,1994年11月正是它相关的第1个 draft 发布的时间。 

4008544e7b8f04c85f317f05622f1adc.png

RFC 1945 版本历史

HTTP/1.0 明确定义了 method 有3个:GET、POST、HEAD。这相对于 HTTP/0.92 似乎还有点退步了,不过这不是重点,毕竟有不少 HTTP/0.92 的method,HTTP/1.1 版本也没有采纳。

HTTP/1.0 最重要的革命性突破有3点:

(1)以标准的形式,正式定义了 HTTP 的报文格式(虽然 HTTP/0.92 已经基本定义了该格式)

(2)扩展了 HTTP 的数据格式,原来的 HTTP/0.9(及 HTTP/0.92)只支持 text/html,而 HTTP/1.0 则支持很多数据格式,比如文本、图像、音频、应用程序等等(text/plain、text/html、text/css、image/jpeg、image/png、image/svg+xml、audio/mp4、video/mp4、application/javascript、application/pdf、application/zip、application/atom+xml)。这也使得 HTTP 中的“Hypertext(HT)”有点过时甚至是错误了,HTTP 如果改名为 HMTP(Hypermedia Transfer Protocol,超媒体传输协议)似乎显得更加合理。不过这都是细节了,历史的惯性使得 HTTP 的名称依然保留至今(并且永传下去)。

(3)扩展了 HTTP 的传输类型,比如 gzip、compress、deflate 等。

HTTP/1.0 虽然取得了3点重大突破,不过令人遗憾的是,在连接层面,相对于  HTTP/0.9,它并没后任何改变。HTTP/1.0 仍然是1个 HTTP Request-Response 需要1个 TCP 连接:每次 HTTP Client 的 Request 之前需要先建立 TCP 连接,而 HTTP Server 回应1个 Response 以后,这个 TCP 连接就关闭了。

考虑到 TCP 连接的三次握手、四次挥手,就可以想象 HTTP 这种连接是多么低效。为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。Connection: keep-alive,这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。这样的话,一个可以复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此这不是根本的解决办法。

6.1.3.3 HTTP/1.1

自1997年1月发布以来,HTTP/1.1 一直是并且到现在仍然是使用最广泛、最流行的 HTTP 版本。从发布时间来看,HTTP/1.1 只比 HTTP/1.0 的发布时间晚了8个月,而如果从起草时间来看,HTTP/1.1 甚至在 HTTP/1.0 发布之前就开始起草了。 

1d6b2da008c4eedd685e4d61e69a6049.png

RFC 2068 版本历史

HTTP/1.0 和 HTTP/1.1 的作者里,至少有两位是相同的,其中一位当然是 WWW 之父蒂姆·伯纳斯·李,而另一位则是提出了 REST 概念的大名鼎鼎的 Roy Thomas Fielding。也许,他们压根就没打算让 HTTP/1.0 真正实施。

说明:REST(Representational State Transfer,表述性状态转移),是 Roy Thomas Fielding 博士在2000年他的博士论文中提出来的一种软件架构风格,一度是程序猿、架构师等屌丝的装逼用语。

从功能角度看,HTTP/1.1 相对于 HTTP/1.0,提出了更多的 method:PUT、PATCH、HEAD、 OPTIONS、DELETE。也正式从这个意义上讲,HTTP/1.0 确实难堪大任,毕竟它的功能还是不完善的。

从数据传输类型看,HTTP/1.1 还提出了分块传输编码(chunked transfer encoding)。

从连接的角度看,HTTP/1.1 引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。

另外,HTTP/1.1 还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。不过管道机制并没有流行起来。

HTTP/1.1 还有其他很多特性,这些我们都放到后面的章节里详细描述,这里就不再一一介绍。

6.1.3.4 HTTP/2

发布于2015年5月的 HTTP/2(RFC 7540)只有主版本号“2”,而没有子版本号(比如 “.0”、“.1”之类的)。事实上 HTTP/2 原来确实名为为 HTTP/2.0,只不过后来 IETF 认为HTTP/2.0 已经很成熟了,没有必要再发布子版本,以后若有有重大改动就直接发布HTTP/3,于是将 HTTP/2.0 改名为 HTTP/2。

从版本号命名这么任性的角度讲,能与之媲美的也许只有华为手机,从 P10 到 P20 再到 P30,中间的数字连看都不看一眼。呵呵,我只是对华为手机开一个小小的玩笑,内心深处还是无比敬佩华为手机的。华为,中国制(智)造的骄傲!

63532ad7be49ff91478d369c6c525519.png

HTTP/2 起源于 Google 的 SPDY(读作“SPeeDY”)协议。SPDY 最初旨在解决HTTP/1.1的线头阻塞问题,于2009被 Google 提出。Google 于2012年又实现了流控制,2013-2014期间实现了流优先级,server push等特性。基于 SPDY 的 HTTP/2 发布以后,Google 宣布放弃对 SPDY 协议的支持,转而支持HTTP/2。

HTTP/1.0 宣称自己是为互联网而生的,这个口号其实有点谦虚,没有 HTTP,没有 WWW,又哪来今天的欣欣向荣的 Internet。

沿袭着一贯的思路,HTTP/2 宣称自己是为物联网而生的,这个口号是不是有点言过其词,我们不去深究,让时间给出答案吧。不过 HTTP/2(SPDY)诞生的最主要的目的,就是为了解决 HTTP/1.1 的传输效率问题。

HTTP/1.1 有哪些问题呢?

虽然 HTTP/1.1 支持了长连接,但是1个连接在任一个时刻只能只能处理1个 Request-Response 交互。这样的话,如果1个Request-Response 交互时间过长,那么后面的 Request-Response 报文只能在那里无奈的等待。这就是所谓的线头阻塞 (Head Of Line Blocking) 问题。虽然 HTTP/1.1 试图用管道机制(pipelining)来解决这个问题,但是管道机制本身还存在很多问题,并不能有效解决线头阻塞。

另外,对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。在某些场景下,这也严重影响传输效率。虽然有一些解决方法,比如分片域名,但是与管道机制一样,这些方法总是解决一个问题的同时,引入更多的问题。

其他还有诸如 HTTP 报文头过大等问题,不一而足,总之在  HTTP/2(SPDY)看来,HTTP/1.1 的传输效率太低,需要改进。

HTTP/2 所做的改进,主要有以下几点:

(1)二进制分帧层 (Binary Framing Layer)

(2)多路复用 (MultiPlexing)

(3)服务端推送 (Server Push)

(4)Header 压缩 (HPACK)

(5)应用层的重置连接

(6)请求优先级设置

(7)流量控制

由于文章主题和篇幅的关系,本文就不一一介绍这些改进特性(日后如果有机会,笔者会专门写一篇 HTTP/2 的文章)。这里稍微处于一种八卦的心态,简单介绍一下 HTTP/2 的二进制分帧层。

在6.1.2节,我们说过,HTTP 的报文格式就是一堆字符串,不像 TCP 等协议那样有逼格。不过 HTTP/2 将报文格式改成了有逼格的样子,如图6-8所示:

850ed5621e2a471880e452428c33d66a.png

图6-8 HTTP/2 报文格式

由于篇幅关系,我们点到为止,就不再介绍 HTTP/2 报文中的具体字段了。不过有一点需要强调的是,虽然报文格式做了改变,但是 HTTP/1.1 各个字段的语义,HTTP/2 还是保持兼容的。因为 HTTP/2 的目标是为了优化 HTTP/1.1 的传输效率,改变报文格式是为了达成此目的的一个手段,并不是为了改变各个字段的语义。

截止到2019年3月17日,全世界已经有34.0%的网站支持 HTTP/2(https://w3techs.com/technologies/details/ce-http2/all/all)。

a5f7d7d2af6f74264508de6e9b08427b.png

Usage of HTTP/2 for websites,17 Mar 2019,W3Tech.com

如果您认为 HTTP/2 是未来的话,那么

未来已来!

6.1.3.5 小结

前文说过,HTTP 这个名字本身没有跟上自己的发展,它早已不是一开始只能传输超文本的协议,而是能够传输包括文本、图像、音频、视频、应用程序等各种格式的数据。另外,HTTP 其实还有一点也没有跟上自己的步伐。刚开始的时候,HTTP 只有一个动作:GET,其目的就是到服务器上检索相关信息,而后来 HTTP 不仅能检索,还能修改、删除服务器上相关信息。所以这个时候如果只说“传输”两字,已经不能概括协议的作用了。

我们感谢这个美丽的错误!

错误

郑愁予

我打江南走过

那等在季节里的容颜如莲花的开落

东风不来,三月的柳絮不飞

你的心如小小的寂寞的城

恰若青石的街道向晚

跫音不响,三月的春帷不揭

你的心是小小的窗扉紧掩

我达达的马蹄是美丽的错误

我不是归人,是个过客……

在历史的长河中,HTTP 终究也是个过客,而不是归人。但是,无论未来是什么样子,她一定会在某个午后,想起 HTTP 这个最熟悉的陌生人。

纵观 HTTP 的发展史,可以梳理为3条主线:传输的目的、传输的内容、传输的效率。

传输的目的,HTTP/0.9 只有1个,那就是 GET。后来发展到 HTTP/1.1,传输的目的基本定稿,一共有8个:GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE。

传输的内容,HTTP/0.9 也只有1个,那就是 text/html。自 HTTP/1.0 开始,HTTP 所能传输的内容就是包罗万象,应用尽有。

与传输效率有关的,可以分为两种,一种是压缩,一种是连接。对于压缩而言,可以是 HTTP/2 才开始支持的报文头压缩,也是可以 HTTP/1.0 就支持的 Transfer-Encoding,比如 gzip 压缩。

相比较而言,HTTP 的连接,可能是 HTTP 发展史中最纠结的地方。从 HTTP/0.9 只支持短链接,到 HTTP/1.1 正式支持长连接,再到 HTTP/2 支持多路复用,甚至到 HTTP/3 将底层的 TCP 替换为 QUIC,无不体现了 HTTP 在连接方面的努力和奋斗。

下面我们就从这几个方面,对 HTTP 各个版本做一个简单的小结,如表6-2所示:

表6-2  HTTP 各版本小结

HTTP/0.9

HTTP/1.0

HTTP/1.1

HTTP/2

HTTP/3

报文格式

The One-Line Protocol

HTTP-message

HTTP-message

二进制

未发布,不涉及

传输的目的

GET

GET、POST、HEAD

GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE

GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE

未发布,不涉及

传输的内容

text/html

包罗万象

包罗万象

包罗万象

未发布,不涉及

传输的效率

压缩

不支持

数据压缩

数据压缩

数据压缩、头部压缩

未发布,不涉及

连接

短连接

短连接(可以非标准地扩展为长连接)

长连接

长连接、多路复用、Server Push

未发布,不涉及

底层协议

TCP

TCP

TCP

TCP

QUIC

6.1.4 HTTP 与 HTTPS、S-HTTP 之间的关系

现在上网,或者更广泛地说,HTTP Client 与 HTTP Server 之间一般运行的是 HTTPS,而不是 HTTP。

HTTPS(超文本传输安全协议),百度百科的解释是:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure。而维基百科的解释是:Hypertext Transfer Protocol Secure ...... The protocol is therefore also often referred to as HTTP over TLS, or HTTP over SSL

让人有一点晕,不是吗?我们还是暂时忘记 HTTPS,先理一理 TLS 和 SSL 之间的关系。

SSL(Secure Sockets Layer,安全套接层)是由 Netscape(网景)公司开发的一个协议,当前最新版本是 SSL 3.0。虽然 SSL 是一个广泛以用的协议,也可以说是事实上的标准,但是从 IETF 的角度来说,真正的标准是 TLS。

TLS(Transport Layer Security,传输层安全协议)当前最新的版本是 TLS 1.3(RFC 8446,发布于2018年8月)。与 TLS 1.3 对应的历史版本就是:

(1)TLS 1.2,RFC 5246,发布于2008年8月

(2)TLS 1.1,RFC 4346,发布于2006年4月

(3)TLS 1.0,RFC 2246,发布于 1999年1月

重点就是 TLS 1.0(TLS 的第1个版本),它完全是基于 SSL 3.0所进行的标准化文档写作(设计)。RFC 2246 是这么说的:

This document and the TLS protocol itself are based on the SSL 3.0 Protocol Specification as published by Netscape. The differences between this protocol and SSL 3.0 are not dramatic, but they are significant enough that TLS 1.0 and SSL 3.0 do not interoperate (although TLS 1.0 does incorporate a mechanism by which a TLS implementation can back down to SSL 3.0).

这段文字还说明,虽然 TLS 1.0 脱胎于 SSL 3.0,但是两者并不能互操作(虽然有规避手段)。所以可以这样理解两者之间的关系:

TLS(1.0)脱胎于 SSL(3.0),但是自己又独立做了发展。TLS 是一个标准协议,SSL 虽然是私有协议,但也是事实上的标准。很多应用都是同时支持这两个协议。

我们用 http://tool.chinaz.com/ 对百度做一个 HTTPS 检测,会发现百度同时支持:SSL 3.0、TLS1.0、TLS 1.1、TLS 1.2。

517cac2e092a8fd6d0d77385f02e338e.png

百度支持的 SSL/TLS

SSL/TLS,正如其名字所暗示的,属于网络安全层面的协议。还是那句话,由于篇幅和主题的关系,本文就点到为止,不再介绍 SSL/TLS 的具体内容。

按照 OSI 七层模型,SSL/TLS 位于第5层会话层,HTTP 位于第7层应用层。HTTP 和 HTTPS 之间的关系,如图6-9所示:

cd4d9709d11d98099c2292350be87421.png

图6-9 HTTP 与 HTTPS 之间的关系

通过图6-9可以看到,HTTP还是那个 HTTP,如果它的下层承载协议是 TCP,那么就称之为 HTTP,如果它的下层承载协议是 SSL/TLS,那么就称之为 HTTPS。不过 HTTP 的默认端口号是80,而 HTTPS 的默认端口号是443。

HTTPS 首先由Netscape(网景)公司于1994年提出,当时 HTTPS 是基于 SSL。随着 TLS 标准化的进行,2000年5月发布的 RFC 2818 正式提出了 HTTPS 的另一种实现:HTTP over TLS:This memo describes how to use TLS to secure HTTP connections over the Internet. Current practice is to layer HTTP over SSL (the predecessor to TLS)

所以,我们现在再来看百度百科关于 HTTPS 的两种英语解释,就能理解其名称的由来:Hyper Text Transfer Protocol over Secure Socket Layer 是 HTTPS 的初始解释,因为当时只有 HTTP over SSL,待到后来有了 HTTP over TLS 以后,HTTPS 更准确的解释应该是 Hypertext Transfer Protocol Secure。

无论哪种英语解释,HTTPS 的汉语翻译都是“超文本传输安全协议”。不过与这个翻译相似的翻译还有一个协议,那就是 S-HTTP。S-HTTP 的英语全称是:Secure Hypertext Transfer Protocol,汉语翻译是“安全超文本传输协议”。

S-HTTP 是由 EIT(Enterprise Integration Technologies)开发的一个协议(EIT于1995年被 Verifone 公司收购),对应的标准文档是 RFC 2660(发布于 1999年8月)。S-HTTP 与 HTTP 之间的关系,如图6-10所示:

6da13051f9382945c3da0146d46bae37.png

图6-10 HTTP与 S-HTTP 之间的关系

通过6-10可以看到,S-HTTP 与 HTTP 位于同一层,而且其下层协议也都是 TCP,两者的端口号也都是80。S-HTTP 是对 HTTP 的一种兼容和改进。HTTP本身是明文传输,S-HTTP仅仅提供了数据的加密机制,比如服务页面的数据,以及用户提交的数据(比如post),其余的协议部分是和原来HTTP是一样的。

从某种角度来说,S- HTTP比SSL更灵活,功能更强大,但是实现较困难,而且使用也更困难,所以 HTTPS 要比 S-HTTP 要更普遍。我们在日常生活中,几乎看不到 S-HTTP。

6.1.5 小结

春风得意马蹄疾,一日看尽长安花。本文走马观花,对 HTTP 的基本网络架构、报文格式、发展历史、安全传输做了简单的梳理。

这个世界,很多事物的存在都是有目的的。要理解 HTTP,首先要理解应用层的含义。从物理层、到数据链路层、网络层、传输层,所有这些层次的网络协议,都是为应用层服务的。没有应用层,这些层的协议就没有意义。而应用层,则是为人类服务的。

人类生产信息、同时也需要交换信息。应用层以下的协议的本质就是连接,而应用层协议的本质就是交换。

交换的目的,交换的内容,交换的效率,交换的安全,无论哪一种应用层协议,都需要回答这四个问题。HTTP 不过是其中一种具体的实现罢了。HTTP 从 HTTP/0.9 发展到 HTTP/1.0、HTTP/1.1、HTTP/2,乃至 HTTP/3,也是为了更好地回答这四个问题。

Internet 让我们这个美丽的蓝色星球,变成了地球村。我们彼此交换着信息,让这个村充满着爱,可是对于这爱的使者,我们却不太能感知到 HTTP 的存在。

HTTP 也就是我们生活中最熟悉的陌生人。不知道本文,能不能让 HTTP 不在自己的思念里沉沦?

5c9b8418ce4e5913c90f54463161a615.png

------------

参考资料:

1、https://zhidao.baidu.com/question/118014376.html

2、https://en.wikipedia.org/wiki/HTTP/3

3、https://hpbn.co/brief-history-of-http/

4、http://www.ruanyifeng.com/blog/2016/08/http.html

5、https://blog.csdn.net/qq_39502316/article/details/75305188

6、https://www.cnblogs.com/qcloud1001/p/9591172.html

7、https://www.jianshu.com/p/e57ca4fec26f

8、https://blog.csdn.net/qq_41735936/article/details/79836931

9、https://blog.csdn.net/redhat7890/article/details/5133759

--------------------

7478a9c61e53f04a8fa799d7ae10f527.png

京东有售

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值