《HTTP权威指南》学习总结1——HTTP协议概述
Posted Kiwi-Yan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《HTTP权威指南》学习总结1——HTTP协议概述相关的知识,希望对你有一定的参考价值。
备注:本文最初是针对我在github pages上搭建的博客写的,很多样式都是自己定制的,所以看起来效果可能不是很好,想获得更佳的阅读体验可以移步我的博客。
引言
本来是计划要翻译HTTP相关rfc文档的,奈何工程量巨大,进度实在太慢,只能放弃,以后有兴趣可以搞一下,因为里面确实有一些很有用的东西,当前学习任务还是讲究效率的。终于在一些因素的影响下,我决定在1个月内看完《HTTP权威指南》这本书。从3月13日开始到4月15日,进度还是拖下了,因为游戏太好玩了。
不说废话,进入正题。
如今我们已进入Web x.0的时代(求x = ?),HTTP协议是Web系统的最基本协议,但是很多开发者都很少系统了解过,殊不知了解HTTP协议可以解决很多开发过程中的疑难杂症。因为自己在两年前学习网络编程的时候想着做个什么东西出来,当时就选了一个HTTP服务器,从此与HTTP结下了不解之缘,工作以后到目前为止主要工作内容就是Web开发,于是又勾起了那段青涩的回忆,也开始想深入了解一下她,于是乎……(对不起我编不下去了),总之,我写下了这几篇博客来作为自己对最近一段时间学习的总结。
什么是HTTP协议?
HTTP的全称是Hypertext Transfer Protocal(超文本传输协议),最初是一帮搞核物理实验的人发明出来用来做文件共享的,没想到竟然成为了因特网的发展史上的一次“革命”,让我们感谢一下T. Berners-Lee(没有他的话我也不知道我是搞什么的)。最新的HTTP/2草案已经由IETF在2015年5月发布,但目前HTTP被使用的版本仍以HTTP/1.1为主,所以我也以介绍HTTP/1.1为主(关键是HTTP/2还没有开始了解,后面可能会写一篇介绍HTTP/2的文章)。
本段信息翻译自rfc1945
HTTP(Hypertext Transfer Protocol,超文本传输协议)是一个应用层协议,它拥有分布式的、协作的、超媒体的信息系统对灵活性及速度的要求。它是一个通用的、无状态的、面向对象的协议,通过对它的请求方法(命令)进行扩展,可以被用于多种用途,例如名称服务器、分布式对象管理系统。HTTP的一个特性是它的数据表现类型允许系统的构建不需要依赖所传输的数据。
HTTP自从1990年就在WWW(World-Wide Web,万维网)上被广泛使用。本段摘自维基百科
名称服务器(英语:name server或nameserver)是指提供域名服务协议的程序或服务器。它可以将“人类可识别”的标识符,映射为系统内部通常为数字形式的标识码。域名系统(DNS)服务器是最著名的名称服务器。
所以我们知道了HTTP实际上主要是用来做资源共享的,它是Web服务器与Web客户端使用的应用层通信协议:Web客户端向Web服务器请求Web资源,Web服务器做一定的处理将资源以及相关信息发给客户端。
那么问题来了,什么是Web资源?
Web资源与URI
所有能提供Web内容的东西都是Web资源,包括静态资源和动态资源。URI提供了一种统一的资源命名方式,它标记了资源的访问方式。URI分为URL和URN,一般情况下,URI指的就是URL。
URI:Uniform Resource Identifier,统一资源标识符
URL:Uniform Resource Locator,统一资源定位符
URN:Uniform Resource Name,统一资源名称
URL拥有固定的语法格式,因方案(scheme)而异,看下面的例子:
HTTP:http://<host>:<port>/<path>?<query>#<frag>
HTTPS:https://<host>:<port>/<path>?<query>#<frag>
FTP:ftp://<user>:<password>@<host>:<port>/<path>;<params>
FILE:file://<host>/<path>
关于URL格式的更详细信息可以参考rfc1738。
注意,方案即代表着协议,URL并不局限于HTTP协议。下面我们详细讨论一下HTTP的URL。
组件 | 意义 | 是否可选 | |
---|---|---|---|
scheme | 方案 | 指明了解析URL的程序使用什么协议,方案名是大小写无关的 | 必须 |
host | 主机 | 标识了资源所在的宿主机器,也就是服务器 | 必须 |
port | 端口 | 说明了服务器正在监听的网络端口 | 有默认值 |
path | 路径 | 指明了资源所在的地方 | 可选 |
query | 查询字符串 | 额外传递给服务器的信息 | 可选 |
frag | 片段 | 表示资源内部的一个片段,仅由客户端使用,客户端不能将片段传送给服务器 | 可选 |
HTTP的URL与HTTPS的URL很像,不过HTTP的端口默认值为80,HTTPS的端口默认值为443。
URL分为相对URL和绝对URL。上面讨论的都是绝对URL,单独的相对URL没有任何意义。相对URL可以通过基础URL来推导成绝对URL。基础URL可能由包含相对URL的资源显式指定(html里的标签可以指定基础URL),如果没有显式指定,则可以由包含相对URL的资源确定。相对URL推导成绝对URL的算法在rfc1808中指定,后来又合并到rfc2396中。
URL由ASCII中除空格外的可打印字符表示,其他的字符我们称之为受限字符或者不安全字符,这些字符在表示URL时需要转义。
客户端应该且只应该对所有受限/不安全字符进行转义,有些恶意的客户端会对额外的字符进行转义以绕过Web过滤程序的模式匹配。
URL中要转义的值最好在ASCII字符范围内(0 ~ 127),包含其他字符可能会导致一些问题。总之,URL对国际化的支持还是不够友好,尽量只使用英文字符。
URL表示了资源的位置,一旦资源被移走,URL就会失效。URN则为特定内容提供了一个稳定的名称,与位置无关。例如urn:ietf:rfc:2616
用来表示文档rfc2616。URN当前仍处于试验阶段,它需要一个架构来解析资源的位置,目前缺乏此类架构。
永久统一资源定位符(PURL)可以用URL来实现与URN相同的功能,引入一个中间层对资源的实际URL登记和跟踪,当使用失效的URL访问时,该中间层重定向到实际的URL上去。
HTTP报文
在计算机网络中,我们一般将应用层要发送的数据称为报文。报文是来自客户端的请求数据或者是来自服务端的响应数据。HTTP报文是基于文本行的格式化数据,尽管这对计算机来说可能不够友好(影响效率又容易出错),可是这也增加了HTTP的可扩展性和可调试性(当我们拿到一条报文时不必去查找二进制与实际意义的对应关系)。这样还是有些好处的。
HTTP规范中要求应该用
<CR><LF>
两个字符(即\r\n
,包含一个回车符和一个换行符)来表示文本行的结束,但并不是所有程序都遵守这个规范,稳健的程序应该接受单个换行符作为行的终止。
HTTP报文由三个部分组成:一个对报文进行描述的起始行(start line),零个或多个说明属性的首部(header),以及可选的、包含数据的实体主体(entity-body,实体的主体部分)。
实体(entity)由实体首部与实体主体组成,实体首部描述了与实体相关的一些属性。如不做特殊说明,下面的“实体的主体部分”、“实体主体”是等价的。
HTTP请求报文(request message)的语法如下:
<method> <request-uri> <HTTP-version>
<headers>
<entity-body>
HTTP响应报文(response message)的语法如下:
<HTTP-version> <status-code> <reason-phrase>
<headers>
<entity-body>
先对各部分的作一下简要说明:
+ 方法 <method>
指明了将要以何种方式来访问由请求URI指定的资源。方法是大小写敏感的。
+ 请求URI <request-uri>
包含资源绝对路径的URL,服务器可以假定自己是URL的主机/端口。
+ HTTP版本 <HTTP-version>
包含HTTP版本的主版本号和次要版本号,如HTTP/0.9,HTTP/1.1等。版本号是为了让通信双方了解彼此的能力。
+ 状态码 <status-code>
由三位数字组成,表明了请求是否被理解或被满足,描述了请求过程中发生的情况。
+ 原因短语 <reason-phrase>
用简短的文字来描述状态代码产生的原因。原因短语不做特殊限制,通常使用通用的描述方便交流。客户端不需要检查或显示原因短语。
+ 首部 <headers>
可以有零个或多个首部,每个首部包含一个名字,后面跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后是一个。首部描述了请求或者响应的一些属性,有些HTTP版本要求报文中必须包含某些特定的首部。
+ 实体主体 <entity-body>
包含一个由任意数据组成的数据块。并不是所有的报文都包含实体主体,由请求方法与响应状态码决定。
注意,起始行和首部都是纯文本的,但实体主体没有此限制。
下面是一个请求报文的例子:
GET /example.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
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
下面是一个响应报文的例子:
HTTP/1.1 200 OK
Date: Sun, 16 Apr 2017 01:40:46 GMT
Server: Apache/2.4.23 (Win64) php/5.6.24
Last-Modified: Sun, 16 Apr 2017 01:40:01 GMT
ETag: "8a-54d3ec0d24918"
Accept-Ranges: bytes
Content-Length: 138
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<title>EXAMPLE</title>
</head>
<body>
<p>Hello, This is an example page.</p>
</body>
</html>
TIPS:借助浏览器的F12调试工具可以看到报文的具体内容哦~
下面稍微详细介绍一下报文的各个部分:
1. 协议版本
HTTP的第一个版本是HTTP/0.9,协议内容非常简单:请求报文只有一个请求行且没有版本描述,响应报文只有实体主体。HTTP/0.9只支持GET方法,而且服务器只能回应HTML格式的字符串,不能回应别的格式。定义它的初衷只是为了获取简单的HTML对象。很快它就被HTTP/1.0取代了。
HTTP/1.0是第一个得到广泛使用的HTTP版本,它于1996年5月发布,定义于rfc1945当中。协议的内容大大增加,添加了版本号、首部、一些额外的方法以及服务器响应的状态码。HTTP/1.0引入了POST方法和HEAD方法,而且任何格式的内容都可以发送了。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。这为互联网的大发展奠定了基础。HTTP/1.0的新特性使得HTTP的可用性大大提高,为了满足商业需要,各个Web客户端和服务器厂商都在向HTTP中添加各种新特性,于是产生了一个HTTP的非正式扩展版本,已经成为非官方的事实标准,通常称之为HTTP/1.0+。
HTTP/1.1对HTTP/1.0+进行改进,主要关注的是设计中的缺陷与语义明确,引入了一些性能优化措施,并删除了一些不好的特性。HTTP/1.1最初于1997年1月发布,定义于rfc2068中,后来又在1999年6月重新修订,形成了现在广为人知的rfc2616。再后来又由多个rfc文档进行补充详细说明。
本段信息翻译自rfc2616
超文本传输协议(HTTP)是一个分布式、协作、超媒体信息系统的应用层协议。在1990年WWW全球信息刚刚起步的时候HTTP就得到了应用。HTTP的第一个版本我们称之为HTTP/0.9,是一种为互联网原始数据传输服务的简单协议。由rfc1945定义的HTTP/1.0进一步完善了这个协议。它允许消息以类MIME消息的格式传送,它包括传输数据的元信息和对请求/响应语义的修饰。但是,HTTP/1.0没有充分考虑到分层代理、缓存的影响和持久连接及虚拟主机的需求。并且随着不完善的HTTP/1.0应用程序的激增,这就迫切需要一个新的版本,以便能使两个通信程序能够确定彼此的真实能力。此规范定义的协议叫做“HTTP/1.1”,这个协议与HTTP/1.0相比,此规范更为严格,以确保各个协议的特征得到可靠实现。
HTTP/2已经在2015年5月发布了草案,定义于rfc7540。这个版本删除了次要版本号,所以你看到的是HTTP/2而不是HTTP/2.0。这个版本将HTTP的语义进行了优化,比如采用了二进制描述。(这个版本的rfc文档已不再由T. Berners-Lee起草,而是由SPDY的相关人员编写)
本段信息翻译自https://http2.github.io/faq/
Q: HTTP/2会替换HTTP/1.x吗?
A: 工作组的目标是HTTP/1.x的典型用途可以使用HTTP/2并看到一些好处。话虽如此,我们不能强迫世界迁移,并且由于人们部署代理和服务器的方式,HTTP/1.x很可能仍然再使用一段时间。
现在已经有一部分客户端服务器已能支持HTTP/2(的部分特性?),但HTTP/1.1在一段时间内仍然会是主流版本。
2. 方法
方法除了HTTP协议预定义的一些请求方法外是可以扩展的,下表列出的方法被大多数商业服务器所实现。
方法 | 描述 | 是否包含实体 | 起始版本 |
---|---|---|---|
`GET` | 从服务器获取一份文档 | 否 | HTTP/0.9 |
`HEAD` | 向服务器获取以GET方法请求时文档的首部 | 否 | HTTP/1.0 |
`POST` | 向服务器发送要处理的数据 | 是 | HTTP/1.0 |
`TRACE` | 对可能经过代理服务器的报文进行跟踪 | 否 | HTTP/1.1 |
`OPTIONS` | 了解服务器可以对资源执行哪些方法 | 否 | HTTP/1.1 |
并不是所有的服务器都实现了所有方法。如果服务器不理解客户端发送的方法,就会返回501或者405的状态码。
HTTP将不会在服务器上产生任何结果的方法称为安全方法(这里的产生结果是指修改数据),GET和HEAD被设计为安全方法。实际上安全方法并非真的不会产生结果,这取决于Web开发者。安全方法被设计的目的在于提示/通知用户不安全的方法可能会导致一些后果(比如点击支付按钮的可能导致账户扣款)。
3. 状态码
方法告诉服务器做什么事情,状态码则告诉客户端发生了什么事情。状态码由三位数字组成,根据第一位数字分成五大类:
+ 1xx:信息型状态码
+ 2xx:成功状态码
+ 3xx:重定向状态码
+ 4xx:客户端错误状态码
+ 5xx:服务端错误状态码
原因短语通常和状态码一起使用,但原因短语是给人看的,比如,对客户端或服务器来说,它对200 OK
和200 Done
的处理方式完全一样。尽管如此,大多数服务器还是会返回HTTP协议推荐的原因短语。
下表列出了一些常见的错误码:
状态码 | 原因短语 | 含义 |
---|---|---|
200 | OK | 成功请求,实体包含所请求的资源 |
304 | Not Modified | 客户端发起条件GET请求,同时所请求资源未修改就返回这个状态码,表明可以使用缓存。返回这个状态码时不应该包含实体主体 |
400 | Bad Request | 告知客户端发了一个错误的请求 |
403 | Forbidden | 服务器拒绝请求,返回实体可能说明拒绝原因,但这个状态码通常是在服务器不想说明拒绝原因的时候使用的 |
404 | Not Found | 请求资源不存在 |
414 | Request URI Too Large | 请求的URI太长,超过服务器能处理的范围 |
500 | Internal Server Error | 服务器遇到了一个妨碍完成请求的错误 |
502 | Bad Gateway | 作为代理或网关的服务器收到了错误响应时使用此状态码\*\* |
** 经验告诉我们,从一个运行良好的站点收到此状态码表明这个站点连接数过多超出了后端服务器处理的能力,也就是网站所支持的并发连接数不够。
当客户端收到了它不理解的状态码时会根据这个状态码所在的大类来确定如何处理响应。
4. 首部
首部和方法、响应码配合工作,来决定客户端和服务器能做什么事情。首部可以分为五个主要类型。
- 通用首部:客户端和服务器都可以使用的首部。
- 请求首部:请求报文特有的首部
- 响应首部:响应报文特有的首部
- 实体首部:对应于实体的主体部分的首部
- 扩展首部:由应用程序开发者创建的非标准首部
下面举几个例子:
类别 | 首部 | 含义 |
---|---|---|
通用首部 | `Date` | 提供日期和时间,说明报文的创建时间 |
`Cache-Control` | 用于控制缓存 | |
请求首部 | `Host` | 说明所请求的服务器主机 |
`If-Modified-Since` | 只有在指定日期后资源被修改过才完成这个请求,否则返回304 | |
响应首部 | `Server` | 服务器应用程序软件的名称和版本 |
实体首部 | `Content-Length` | 说明实体主体的长度 |
`Content-Type` | 说明实体的MIME类型 | |
`Last-Modified` | 说明实体的最后一次修改日期和时间 |
理解HTTP协议的关键在于对HTTP在不同场景所产生报文的理解,也就是理解不同的状态码、首部具体应用场景。后面几篇文章将会介绍一些重点的模块,想要了解更多可以继续关注我的博客,参考资料中给出了更为完整更为权威的文档,也可以读一下。
HTTP的复杂性主要在于通信的客户端或服务器所支持的HTTP协议可能不同,或者没有正确实现协议要求的某些内容。
到现在为止我们已经知道HTTP报文的基本组成,但是HTTP协议在网络中是如何工作的呢?
HTTP事务
HTTP协议是应用层的通信协议,它依赖传输层的TCP协议来完成报文的发送与接收。TCP协议是面向字节流的可靠的传输层协议,它可以保证数据无误的从发送端到达接收端,因此我们在使用HTTP协议的时候,无需考虑报文是否被正确传输,这些事情TCP都帮我们做了。
HTTPS协议是基于SSL/TLS的HTTP协议。抽象的看,HTTPS主要是在TCP和HTTP之间加了一层“安全层”(确切来说应该是OSI中的表示层和会话层)利用SSL/TLS来对数据进行加密解密。
+-------------------+
| HTTP |
+-------------------+ |-------------------|
| HTTP | | SSL or TLS |
|-------------------| |-------------------|
| TCP | | TCP |
|-------------------| |-------------------|
| IP | | IP |
|-------------------| |-------------------|
| Network Interface | | Network Interface |
+-------------------+ +-------------------+
HTTP协议栈 HTTPS协议栈
HTTP在传输数据前,先建立TCP连接,连接建立好后就在客户端和服务器间形成了一条虚拟电路,HTTP报文可以沿着这条虚拟电路到达指定位置,报文发送完毕可以根据具体情况决定是否需要关闭连接。
关于TCP的信息这里不做过多阐述,我们重点关注HTTP的工作过程。
我们将一条客户端发送到服务器的请求报文与服务器返回给客户端的响应报文合起来称为一个HTTP事务。
最初一个TCP连接仅处理一个HTTP事务。后来为了优化性能提出了持久连接的概念。尽管如此,我们仍需知道HTTP协议是无状态的,也就是说在多个HTTP事务中,每个事务都是独立的,无论TCP连接是持久的还是完成一个事务就断开,HTTP都不会记录此事务的连接状态(对比FTP,FTP在进行用户认证后会一直保持和这个用户的会话,直至关闭连接,但HTTP不会)。
一个HTTP事务的处理过程举例:
1. 客户端从URL中解析出服务器的主机名
2. 客户端将服务器主机名转化为IP地址
3. 客户端从URL中解析出端口号(如果有)
4. 客户端建立与服务器的TCP连接
5. 客户端向服务器发送一条HTTP请求报文
6. 服务器处理客户端发送的请求,生成响应报文发回客户端
7. 客户端收到响应报文,关闭连接
Web结构组件
在这篇文章中,我不停提到服务器和客户端,那么到底什么是服务器和客户端,HTTP的服务器和客户端到底都有哪些呢?
首先得声明,服务器和客户端既可以指软件应用程序,也可以指运行对应软件的硬件设备,可以根据语境区分出具体含义。下面的解释主要针对软件的概念来描述。
1. 服务器(server)
Web服务器是Web资源的拥有者,它可以处理来自Web客户端的请求。这是一个被动的过程,Web服务器不会主动向Web客户端发送报文。Web服务器因为使用HTTP作为通信协议,所以也称为HTTP服务器。常见的Web服务器有nginx、Apache httpd、Apache tomcat、Lighttpd,Microsoft IIS等等。HTTP响应报文中的Server首部表明了服务器的名称和版本。
值得一提的是上面说到的Apache tomcat和Microsoft IIS,它们是一种应用服务器,也可以提供与HTTP服务器相同的功能。不过因为它们主要处理动态内容,所以处理静态页面的性能远远不如Nginx、httpd等专注处理静态页面的HTTP服务器。在实际应用中,通常会将其作为Nginx/httpd的后端服务器来专门处理动态内容。
2. 客户端(client)
Web客户端可以发送HTTP请求报文的程序,通常还会解析收到的HTTP响应报文。最常见的Web客户端就是各种浏览器。除了浏览器还有一类自动发送HTTP请求的程序,我们将其称为Web机器人(也叫爬虫,网络蜘蛛)。某些客户端程序如Telnet也可以当作Web客户端来使用。
可以代替用户生成请求报文的客户端,我们称之为用户代理(user agent),Web客户端主要以用户代理为主,很少有人会主动自己编写请求报文发送给服务器。用户代理在发送请求的时候通常会带上User-Agent首部来说明其信息。至于上面我举例的请求报文中为什么既有Mozilla又有AppleWebKit又有Safari,我表示也是亿脸蒙蔽,实际上我使用的浏览器是Chrome。
除了服务器与客户端,还有一些其他Web组件。
3. 代理(proxy)
代理是位于服务器与客户端之间的HTTP中间实体,可以负责转发流量、内容过滤、性能优化等功能。也称为代理服务器。想了解代理的更多细节请继续关注我的博客。
关于Agent与Proxy
两者翻译成中文都是代理,这很容易引起困惑。可以这么理解:Agent是客户端,Proxy既是服务器又是客户端(即使说成是代理服务器仍不能掩饰它充当客户端角色的本质)
4. 缓存(cache)
缓存是一种特殊的代理服务器,它可以将经过代理的Web响应复制保存起来,以便下次更快地提供服务。正确部署和使用缓存可以很大的提高效率。缓存也叫Web缓存或者代理缓存。
5. 网关(gateway)
网关分为协议网关和资源网关。
协议网关相当于一个协议转换程序,比如用户发送了一个获取文件请求,服务器可能通过HTTP/FTP网关从FTP服务器上将文件获取到发送给客户端。
资源网关则指应用程序获取另一个程序提供的数据,我们称之为网关API。第一个流行的网关API是CGI,由于性能的问题,人们又开发出了fastcgi。(关于CGI相关理论的学习还在我的计划列表中,暂时没法提供更多信息)
6. 隧道(tunnel)
Web隧道可以通过HTTP连接来发送非HTTP的流量。至于具体实现方式我还没有理解清楚。。
More…
[1] rfc1945(HTTP/1.0):https://www.ietf.org/rfc/rfc1945.txt
[2] rfc2616(HTTP/1.1):https://www.ietf.org/rfc/rfc2616.txt
[3] rfc7540(HTTP/2):https://www.ietf.org/rfc/rfc7540.txt
[4] 关于HTTP/2的一些常见问题:https://http2.github.io/faq/
[5] Nginx官网:http://nginx.org/
Next. 《HTTP权威指南》学习总结2——代理和缓存
以上是关于《HTTP权威指南》学习总结1——HTTP协议概述的主要内容,如果未能解决你的问题,请参考以下文章