HTTP权威指南笔记
读书有两种境界,第一种境界是将书读薄,另一种是读厚。本篇文章就是HTTP权威指南的读书笔记,算是读书的第一重境界,将厚书读薄。文章对HTTP的一些关键概念做了比较详细的概述,通读一遍之后,会对HTTP有个总体认识。然后你可以根据文章中的关键点,去查找更详细的细节。这就是读书的第二重境界,将书读厚。
HTTP(hypertext transfer protocol,超文本传输协议)是万维网进行通信时所使用的协议方案。HTTP有很多应用,但最著名的是用于Web浏览器和Web服务器之间的双工通信。
HTTP概述
Web浏览器、服务器和相关的Web应用程序都是通过HTTP相互通信。HTTP是现代全球因特网中使用的公共语言。
HTTP-因特网的多媒体信使
每天都有数亿JPEG图片、HTML页面、文本文件、MPEG电影、WAV音频文件、java小程序和其他资源在因特网游弋。HTTP可以从全世界的Web服务器上将这些信息迅速、便捷、可靠的传输到Web浏览器上。
HTTP使用的是可靠的数据传输协议,它能够确保数据在传输过程中不会被损坏或者产生混乱。对开发容易来说,无需担心HTTP通信会在传输过程中被破坏、复制或者产生畸变。开发人员可以专注于程序特有细节的编写,而不是考虑因特网中存在的一些缺陷和问题。
Web客户端和服务器
Web内容都是存储在Web服务器上。Web服务器使用HTTP协议,因此还可以称为HTTP服务器。HTTP客户端向服务器发出请求,服务器会在HTTP响应中回送所请求的数据。
最常见的HTTP客户端就是浏览器。浏览器向服务器请求HTTP对象,并将对象显示在屏幕上。
媒体类型
因特网有很多的数据类型,HTTP给每种要通过Web传输的对象都打上MIME类型(MIME type)的数据格式标签。最初设计MIME(Multipurpose Internet Mail Extension,多用途因特网邮件扩展)是为了解决不同电子邮件系统之家搬移报文时存在的问题。
Web服务器会为所有的HTTP对象数据附一个MIME类型。当Web浏览器从服务器取回一个对象时,会去查看相关的MIME类型,看看它是否知道应该如何处理这个对象。大多数浏览器都可以处理上百种常见对象:显示图片、解析并格式化HTML文件、播放音频文件、或者运行外部软件来处理特殊格式数据。
MIME类型是一种文本标记,表示一种主要的对象类型和一个特定的子类型,中间由一条斜杠来分隔。
例如:HTML格式的文本文档 text/html类型来标记。
详细的MIME学习可以参考这篇文章 。
URI
每个Web服务器都有一个名字,这样客户端就可以说明它们感兴趣的资源是什么。服务器资源名被统一称为统一资源标识符(Uniform Resource Identifier,URI)。URI有两种形式:分别是URL和URN。
- URL
统一资源定位符(URL,Uniform Resource Locator)是资源标识符最常见的形式。
大部分URL都遵循一种标准格式,这种格式包含三个部分。
1.URL的第一部分称为方案(scheme),说明访问资源所使用的协议类型。这部分通常是HTTP或者HTTPS(http://)。
2.第二部分给出了服务器的因特网地址(www.joes-hardware.com)。
3.其余部分指定了Web服务器上的某个资源(比如,/specials/saw-blade.gif)。
现在几乎所有的URI都是URL。
- URN
URI的第二种形式就是URN(统一资源名)。URN是作为特定内容的唯一名称使用的,与目前的资源所在地无关。使用这些与>位置无关的URN,就可以将资源四处搬动。通过URN,还可以用同一个名字通过多种网络访问协议来访问资源。
URN仍然处于试验阶段,还未大范围使用。除非特殊说明,否则这里的都是用URL来指定URI。
事务
一个HTTP事务由一条请求命令和一个响应结果组成。这种通信通过名为HTTP报文(HTTP message)的格式化数据块进行。
- 方法
HTTP支持几种几种不同的请求命令,这些命令被称为HTTP方法(HTTP method)。每条HTTP请求报文都包含一个方法。这个方法会告诉服务器要执行什么动作(获取一个Web页面、运行一个网关程序、删除一个文件等)。
常见的HTTP方法:
HTTP方法 | 描述 |
---|---|
GET | 从服务器向客户端发送命名资源 |
PUT | 将来自客户端的数据存储到一个命名的资源服务器中 |
DELETE | 从服务器中删除命名资源 |
POST | 将客户端数据发送到一个服务器网关应用程序 |
HEAD | 仅发送命名资源响应中的HTTP首部 |
- 状态码
每条HTTP响应报文返回时都会携带一个状态码。状态码是一个三位数字的代码,告知客户端请求是否成功,或者是否需要采取其他动作。
常见的HTTP状态码:
HTTP状态码 | 描述 |
---|---|
200 | OK, 文档正确返回 |
302 | Redirect(重定向)。到其他地方去获取资源 |
404 | Not Found(没找到),无法找到这个资源 |
- Web页面可以包含多个对象
应用程序完成一项任务时通常会发布多个HTTP事务。比如,Web浏览器会发布一系列的HTTP事务来获取并显示一个包含丰富图片的Web页面。浏览器会执行一个事务来获取描述页面布局的HTML”框架“,然后发布另外的HTTP事务来获取每个嵌入式图片、图像面板、Java小程序等。这些嵌入式资源甚至可能位于不同的服务器。因此,一个Web页面通常并不是单个资源,而是一组资源的集合。
报文
HTTP报文都是由一行行的简单字符串组成。HTTP报文都是纯文本,不是二进制代码。
从Web客户端发往Web服务器的HTTP报文称为请求报文(request message)。从服务器发往客户端的报文称为响应报文(reponse message),此外没有其他类型的HTTP报文。请求报文和响应报文格式类似。
HTTP报文包括以下三部分:
- 起始行
报文的第一行就是起始行,在请求报文中用来说明要做些什么,在响应报文中说明出现了什么情况。
- 首部字段
起始行后面有0个或者多个首部字段。每个字段都包含一个名字和一个值,为了便于解析,两者之间用冒号(:)分隔。首部以一个空行结束。添加一个首部字段和添加新行一样简单。
- 主体
空行之后就是可选的报文主体了,其中包含了所有类型的数据。请求主体中包括了要发给Web服务器的数据,响应主体中装载了要返回给客户端的数据。起始行和首部都是文本形式且都是结构化的,而主体则不同,主体可以包含任意的二进制数据(图片、视频、音频、软件程序)。当然,主体还可以包含文本。
连接
- TCP/IP
HTTP是个应用层协议。HTTP无需关心网络通信的具体细节;它把联网的细节都给了通用、可靠的因特网传输协议TCP/IP。
TCP提供了:
- 无差错的数据传输。
- 按序传输(数据总是按照发送的顺序到达);
- 分段的数据流(可以在任意时刻以任意大小将数据发送出去)。
因特网自身是基于TCP/IP的,它是全世界计算机网络常用的层次化分组交换网络协议集。TCP/IP 隐藏了各种网络和硬件的特点及弱点,使各种类型的计算机和网络都能够进行可靠地通信。
只要建立了TCP连接,客户端和服务器之间的报文交换就不会丢失、被破坏、也不会出现接收时乱序。HTTP协议位于TCP的上层。HTTP使用TCP来传输其报文数据。
- 连接、IP地址及端口号
在HTTP客户端向服务器发送报文之前,需要用网际协议(Internet Prococol,IP)地址和端口号在客户端和服务器之间建立一条TCP/IP连接。
建立一条TCP连接的过程与给公司办公室的某个人打电话的过程类似。首先,要拨打公司的电话号码。这样就能进入正确的机构了。其次,拨打要联系的那个人的分机号。
最初怎么获取服务器的IP地址呢?当然是通过URL。
先看几个URL:
http://207.200.83.29:80/index.html
http://www.netscape.com:80/index.html
http://www.netscape.com/index.html
第一个 URL 使用了机器的 IP 地址,207.200.83.29 以及端口.第二个 URL 没有使用数字形式的 IP 地址,它使用的是文本形式的域名,或者称为主机名(www.netscape.com) 。主机名就是 IP 地址比较人性化的别称。可以通过一
种称为域名服务(Domain Name Service,DNS)的机制方便地将主机名转换为 IP地址,这样所有问题就都解决了。
最后一个 URL 没有端口号。HTTP 的 URL 中没有端口号时,可以假设默认端口号是 80。有了 IP 地址和端口号,客户端就可以很方便地通过 TCP/IP 进行通信了。
步骤如下:
(a) 浏览器从 URL 中解析出服务器的主机名;
(b) 浏览器将服务器的主机名转换成服务器的 IP 地址;
(c) 浏览器将端口号(如果有的话)从 URL 中解析出来;
(d) 浏览器建立一条与 Web 服务器的 TCP 连接;
(e) 浏览器向服务器发送一条 HTTP 请求报文;
(f) 服务器向浏览器回送一条 HTTP 响应报文;
(g) 关闭连接,浏览器显示文档。
- 使用Telnet实例
Telnet程序可以将键盘连接到某个目标TCP端口,并将此TCP端口的输出回送到显示屏上。Telnet常用于远程终端会话,但它几乎可以连接所有的TCP服务器,包括HTTP服务器。
可以通过Telnet程序直接与Web服务器进行对话。通过Telnet可以打开一条到某台机器上某个端口的TCP连接,然后直接向端口输入一些字符。Web服务器会将Telnet程序作为一个Web客户端来处理,然后回送给TCP连接的数据会显示在屏幕上。
实际例子:Telnet获取URL http://www.joes-hardware.com:80/tools.html 所指向的文档
Telnet 会查找主机名并打开一条连接,连接到在 www.joes-hardware.com 的端口 80上监听的 Web 服务器。这条命令之后的三行内容是 Telnet 的输出,告诉我们它已经建立了连接。
然后我们输入最基本的请求命令 GET/tools.html HTTP/1.1 ,发送一个提供了源端主机名的 Host 首部,后面跟上一个空行,请求从服务器 www.joes-hardware.com 上获取资源 tools.html。随后,服务器会以一个响应行、几个响应首部、一个空行和最后面的 HTML 文档主体来应答。
要明确的是,Telnet 可以很好地模拟 HTTP 客户端,但不能作为服务器使用。而且对 Telnet 做脚本自动化是很繁琐乏味的。如果想要更灵活的工具,可以去看看 nc(netcat) 。通过 nc 可以很方便地操纵基于 UDP 和 TCP 的流量(包括 HTTP) ,还可以为其编写脚本。更多细节参见 http://www.bgw.org/tutorials/utilities/nc.php
协议版本
目前HTTP有几个协议版本。
- HTTP/0.9
HTTP 的 1991 原型版本称为 HTTP/0.9。这个协议有很多严重的设计缺陷,只应该用于与老客户端的交互。HTTP/0.9 只支持 GET 方法,不支持多媒体内容的MIME 类型、各种 HTTP 首部,或者版本号。HTTP/0.9 定义的初衷是为了获取简单的 HTML 对象,它很快就被 HTTP/1.0 取代了。
- HTTP/1.0
1.0 是第一个得到广泛使用的 HTTP 版本。HTTP/1.0 添加了版本号、各种 HTTP首部、一些额外的方法,以及对多媒体对象的处理。HTTP/1.0 使得包含生动图片的 Web 页面和交互式表格成为可能,而这些页面和表格促使万维网为人们广泛地接受。这个规范从未得到良好地说明。在这个 HTTP 协议的商业演进和学术研究都在快速进行的时代,它集合了一系列的最佳实践。
- HTTP/1.0+
在 20 世纪 90 年代中叶,很多流行的 Web 客户端和服务器都在飞快地向 HTTP中添加各种特性,以满足快速扩张且在商业上十分成功的万维网的需要。其中很多特性,包括持久的 keep-alive 连接、虚拟主机支持,以及代理连接支持都被加入到 HTTP 之中,并成为非官方的事实标准。这种非正式的 HTTP 扩展版本通常称为 HTTP/1.0+。
- HTTP/1.1
HTTP/1.1 重点关注的是校正 HTTP 设计中的结构性缺陷,明确语义,引入重要的性能优化措施,并删除一些不好的特性。HTTP/1.1 还包含了对 20 世纪 90 年代末正在发展中的更复杂的 Web 应用程序和部署方式的支持。HTTP/1.1 是当前使用的 HTTP 版本。
- HTTP-NG(又名 HTTP/2.0)
HTTP-NG 是 HTTP/1.1 后继结构的原型建议,它重点关注的是性能的大幅优化,以及更强大的服务逻辑远程执行框架。HTTP-NG 的研究工作终止于 1998 年,编写本书时,还没有任何要用此建议取代 HTTP/1.1 的推广计划。
Web结构组件
前面重点介绍了Web应用程序(Web客户端和Web服务器)是如何相互发送报文来实现基本事务处理的。因特网上还有一些其他的应用程序。下面一一介绍。
- 代理
代理位于客户端和服务器之间的HTTP中间实体。接收所有客户端的 HTTP 请求,并将这些请求转发给服务器(可能会对请求进行修改之后转发) 。对用户来说,这些应用程序就是一个代理,代表用户访问服务器。
出于安全考虑,通常会将代理作为转发所有 Web 流量的可信任中间节点使用。代理还可以对请求和响应进行过滤。比如,在企业中对下载的应用程序进行病毒检测,或者对小学生屏蔽一些成人才能看的内容。
- 缓存
Web 缓存(Web cache)或代理缓存(proxy cache)是一种特殊的 HTTP 代理服务器,可以将经过代理传送的常用文档复制保存起来。下一个请求同一文档的客户端就可以享受缓存的私有副本所提供的服务了.
客户端从附近的缓存下载文档会比从远程 Web 服务器下载快得多。HTTP 定义了很多功能,使得缓存更加高效,并规范了文档的新鲜度和缓存内容的隐私性。
- 网关
网关(gateway)是一种特殊的服务器,作为其他服务器的中间实体使用。通常用于将 HTTP 流量转换成其他的协议。网关接受请求时就好像自己是资源的源端服务器一样。客户端可能并不知道自己正在与一个网关进行通信。
例如,一个 HTTP/FTP 网关会通过 HTTP 请求接收对 FTP URI 的请求,但通过 FTP协议来获取文档 。得到的文档会被封装成一条 HTTP 报文,发送给客户端。第 8 章将探讨网关。
- 隧道
隧道(tunnel)是建立起来之后,就会在两条连接之间对原始数据进行盲转发的HTTP 应用程序。HTTP 隧道通常用来在一条或多条 HTTP 连接上转发非 HTTP 数据,转发时不会窥探数据。
HTTP 隧道的一种常见用途是通过 HTTP 连接承载加密的安全套接字层(SSL,Secure Sockets Layer)流量,这样 SSL 流量就可以穿过只允许 Web 流量通过的防火墙了。如图所示,HTTP/SSL 隧道收到一条 HTTP 请求,要求建立一条到目的地址和端口的输出连接,然后在 HTTP 信道上通过隧道传输加密的 SSL 流量,这样就可以将其盲转发到目的服务器上去了。
- Agent代理
用户 Agent 代理(或者简称为 Agent 代理)是代表用户发起 HTTP 请求的客户端程序。所有发布 Web 请求的应用程序都是 HTTP Agent 代理。到目前为止,我们只提到过一种 HTTP Agent 代理:Web 浏览器,但用户 Agent 代理还有很多其他类型。用fiddler抓包找头部信息会发现类似User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36
比如,有些自己会在 Web 上闲逛的自动用户 Agent 代理,可以在无人监视的情况下发布 HTTP 事务并获取内容。这些自动代理的名字通常都很生动,比如“网络蜘蛛”(spiders)或者“Web 机器人” (Web robots) 。网络蜘蛛会在 Web 上闲逛,搜集信息以构建有效的 Web 内容档案,比如一个搜索引擎的数据库或者为比较购物机器人生成的产品目录。
URL和资源
因特网资源
URL是浏览器寻找信息所需的资源位置。URI是一类更通用的资源标识符,URL是URI的一个子集。URI包括URL和URN。
URL语法
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
- scheme
访问服务器以获取资源时要使用的协议 默认值 无 - user
一些方案访问资源时需要的用户名 默认值 匿名 - password
用户名后面可能要包含密码,中间由冒号分隔 默认值 <E-mail地址> - host
资源宿主服务器的主机名或点分IP地址 默认值 无 - port
资源宿主服务器正在监听的端口号,很多方案都有默认端口号(HTTP默认端口号为80) 默认值 每个方案特有 - path
服务器上资源的本地名,由一个"/"将其与前面的URL组件分隔开来.路径组件的语法与服务器和方案有关 默认值 无 - params
一些方案会用这个组件指定输入参数. 参数为名/值对. URL中可以包含多个参数字段,它们相互以及与路径的其余部分之间用分号(;)分隔 默认值 无 - query
一些方案会用这个组件传递参数以激活应用程序.查询组件的内容没有通用格式.用符号"?"将其与URL的其余部分分隔开来 默认值 无 - frag
一小片或一部分资源的名字.引用对象时,不会将frag字段传送给服务器。这个字段是在客户端内部使用的.通过字符"#"将其与URL其余部分分隔开来 默认值 无
http://www.joes-hardware.com/hammers;sale=false/index.html;graphic=ture
这个例子就有两个路径段,hammers和index.html。hammers路径段的参数是sale,值为false。index.html段有参数graphics,值为true。
http://www.joes-hardware.com/inventory-check.cgi?item=12731
问号右边的内容称为查询组件。URL的查询组件和标志网关资源的URL路径组件一起被发送给网关资源。基本上可以将网关当作访问其他应用程序的访问点。
HTTP服务器只处理整个对象,而不是对象的片段,客户端不能将片段传送给服务器,浏览器获得整个资源后,会根据片段来显示你感兴趣的内容。
HTTP报文
报文流
HTTP报文是在HTTP应用程序之间发送的数据块。这些数据以文本形式的元信息(meta-information)开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。报文在客户端、服务器、代理之间流动。
报文流入源端服务器
HTTP使用术语流入(inbound)和流出(outbound)来描述事务处理(transaction)的反向。
流入:客户端->服务器
流出:服务器->客户端报文向下游流动
不管是请求报文还是响应报文,所有报文都会向下游(downstream)流动。所有报文的发送者都在接收者的上游(upstream)。对请求报文来说,代理1位于代理3上游,但对于响应报文来说,它就位于代理3的下游。
报文的组成部分
三部分组成:
1.对报文进行描述的起始行(start line)
2.包含属性的首部(header)块
3.可选的、包含数据的主体(body)部分
- 报文的语法
请求报文
<method> <request-URL> <version>
<headers>
空行(CRLF)
<entity-body>
响应报文
<version> <status> <reason-phrase>
<headers>
空行(CRLF)
<entity-body>
- method
客户端希望服务器对资源执行的动作。是一个单独的词,比如GET、HEAD或POST。 - request-URL
命名了所请求资源或者URL路径组件的完整URL。如果直接与服务器进行对话,只要URL的路径组件是资源的绝对路径,通常不会有什么问题。 - version
报文所使用的HTTP版本 - status-code
这三位数字描述了请求过程中所发生的情况。 - 原因短语 reason-phrase
数字状态码的可读版本,包含行终止序列之前的所有文本。 - header
可以有0个或多个首部。每个首部都包含一个名字,然后跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后是一个CRLF(回车换行)。首部是由一个空行(CRLF)结束的。表示首部列表的结束和实体主体的开始。
注意,一组HTTP首部总是应该以一个空行(CRLF)结束,甚至即使没有首部和实体的主体部分也应该如此。
起始行
- 请求行
包含一个方法和一个请求URL,这个方法描述服务器该执行的操作,请求URL描述了要对哪个资源执行这个方法。所有这些字段都是由空格符分隔。 - 响应行
响应报文承载了状态信息和操作产生的所有结果数据,将其返回给客户端。响应报文的起始行称为响应行。
常用HTTP方法
方法 | 描述 | 是否包含主体 |
---|---|---|
GET | 从服务器获得一份文档 | 否 |
HEAD | 只从服务器获得响应报文的首部 | 否 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 是 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 | 否 |
OPTIONS | 决定在服务器上可以执行哪些方法 | 否 |
DELETE | 从服务器上删除一份文档 | 否 |
- 状态码
状态码是在每条响应报文的起始行总返回的。会返回一个数字状态和一个可读的状态。数字码便于程序进行差错处理,而原因短语则更便于人们理解。
状态码分类
整体范围 | 已定义范围 | 分类 |
---|---|---|
100~199 | 100~101 | 信息提示 |
200~299 | 200~206 | 成功 |
300~399 | 300~305 | 重定向 |
400~499 | 400~415 | 客户端错误 |
500~599 | 500~505 | 服务器错误 |
常见的状态码
状态码 | 原因短语 | 含义 |
---|---|---|
200 | ok | 成功,请求的所有数据都在响应主体中 |
401 | unauthorized(未授权) | 需要输入用户名和密码 |
404 | not found | 服务器无法找到所请求的URL对应的资源 |
首部分类
1.通用首部:既可以出现在请求报文中,也可以出现在响应报文中。
2.请求首部:提供更多有关请求的信息
3.响应首部:提供更多有关响应的信息
4.实体首部:描述主体的长度和内容,或者资源本身
5.扩展首部:规范中没有定义的新首部。
具体的每个首部用法,可以参考HTTP权威指南options
options方法请求Web服务器告知其支持的各种功能。可以询问服务器支持哪些方法,或者对某些特殊资源的支持方法。这为客户端应用程序提供了一种手段,使其不用实际访问那些资源就能判定访问各种资源的最优方式。重定向状态码
重定向状态码要么告知客户端使用替代位置来访问他们所感兴趣的资源,要么就提供了一个替代的响应而不是资源的内容。如果资源已被移动,可发送一个重定向状态码和一个可选的location首部来告知客户端资源已被移走,以及现在在哪里可以找到它。
连接管理
TCP连接
TCP的可靠数据管道
TCP会按序、无差错的承载HTTP数据
TCP流是分段、由IP分组传送
TCP数据是通过IP分组(IP数据报)的小数据块来发送的。HTTP就是”HTTP over TCP over IP“这个协议栈中的最顶层了。HTTPS就是在HTTP和TCP之间插入了一个(TLS或者SSL)密码加密层。
HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流分成称作段的小数据块,并将段封装在IP分组中,通过因特网传输。所有这些工作由TCP/IP软件来处理,HTTP程序员看不到。
每个TCP段都是由IP分组承载,从一个IP地址发送到另一个IP地址。每个IP分组中包括:
- 一个IP分组首部(通常为20字节)
- 一个TCP段首部(通常为20字节)
- 一个TCP数据块(0或者多个字节)
IP首部包含了源和目的IP地址、长度和其他一些标记。TCP段的首部包含了TCP端口号、TCP控制标记,以及用于数据排序和完整性检查的一些数字值。
保持TCP连接的正确运行
在任意时刻计算机都有几条TCP连接处于打开状态。TCP是通过端口号来保持这些连接正确运行。
端口号类似分机号。IP能让你连接到正确的计算机,端口号则可以将你连接到正确的应用程序上。TCP连接通过4个值来识别:
<源 IP 地址、 源端口号、 目的IP地址、目的端口号>
这四个值一起唯一定义了一条连接。两条不同的TCP连接不能拥有4个完全相同的地址组件值。
四条TCP连接
有些连接共享了相同目的端口号(C和D都是使用80端口)。有些连接使用了相同的源IP。有些使用了相同目的IP。但没有两个连接4个值都一样。
TCP套接字编程
套接字API允许用户创建TCP的端口数据结构,并将这些端点与远程服务器的TCP端口进行连接,并对数据进行读写。TCP API隐藏了所有底层网络协议的握手细节,以及TCP数据流与IP分组之间的分段和重装细节。
TCP建立连接就需要三次握手才能建立,就是之前经常考的三次握手。
TCP关闭连接需要四次挥手。有比较好的文章讲解这个问题。
TCP时延主要在于:
1.TCP的建立连接的握手
2.TCP的慢启动
3.数据聚集的Nagle算法
4.用于捎带确认的TCP延迟确认算法
5.TIME_WAIT时延和端口耗尽
具体细节可看HTTP权威指南
HTTP连接的处理
串行事务处理时延
- 并行连接:多条TCP连接发起并发的HTTP请求
- 持久连接:重用TCP连接,以消除连接及关闭时延
- 管道化连接:通过共享的TCP连接发起并发的HTTP请求。
具体细节可看HTTP权威指南
代理
Web代理服务器是代表客户端完成事务的中间人。没有Web代理,HTTP客户端和服务器直接对话。HTTP代理服务器既是客户端,也是服务器。代理服务器必须像Web服务器一样,正确处理请求和连接,然后返回响应。同时,代理自身要向服务器发送请求。
代理和网关对比
代理连接的是两个或者多个使用相同协议的应用程序,而网关连接的是两个或者多个使用不同协议的端点。
假如我通过代理访问 A 网站,对于 A 来说,它会把代理当做客户端,完全察觉不到真正客户端的存在,这实现了隐藏客户端 IP 的目的。
给浏览器显式的指定代理,需要手动修改浏览器或操作系统相关设置,或者指定 PAC(Proxy Auto-Configuration,自动配置代理)文件自动设置,还有些浏览器支持 WPAD(Web Proxy Autodiscovery Protocol,Web 代理自动发现协议)。显式指定浏览器代理这种方式一般称之为正向代理,浏览器启用正向代理后,会对 HTTP 请求报文做一些修改,来规避老旧代理服务器的一些问题。
还有一种情况是访问 A 网站时,实际*问的是代理,代理收到请求报文后,再向真正提供服务的服务器发起请求,并将响应转发给浏览器。这种情况一般被称之为反向代理,它可以用来隐藏服务器 IP 及端口。一般使用反向代理后,需要通过修改 DNS 让域名解析到代理服务器 IP,这时浏览器无法察觉到真正服务器的存在,当然也就不需要修改配置了。反向代理是 Web 系统最为常见的一种部署方式。
共有代理缓存
公有缓存是特殊的共享代理服务器,被称为缓存代理服务器(caching proxy server,或者更常见的被称为代理缓存(proxy cache)。代理缓存会从本地缓存中提供文档,或者代表用户与服务器联系。公有缓存会接受来自多个用户的访问,所以可以有效的减少冗余流量。
代理缓存的层次结构
层次化缓存是有意义的。较小缓存未命中的请求会向较大父缓存(parent cache)。
缓存的处理步骤
1.接收-缓存从网络中读取抵达的请求报文
2.解析-缓存对报文进行解析,提取URL和各种首部
3.查询-缓存查看是否有本地副本可用,如果没有,就读取一份副本(并将其保存在本地)
4.新鲜度检测-缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有更新
5.创建响应-缓存会用新的首部和已缓存的主体来构建一条响应报文
6.发送-缓存通过网络将响应发回给客户端
7.日志-缓存可选的创建一个日志文件条目来描述这个事务。
保持副本的的新鲜
HTTP有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况,保持已缓存数据和服务器数据之间充分一致。HTTP将这些简单机制称为文档过期(document expiration)和服务器再验证(server revalidation)。
- 文档过期
通过特殊的HTTP cache-control首部和expires首部,HTTP协议让原始服务器向每个文档附加了一个过期日期。在缓存文档过期之前,缓存可以任意使用副本,而无需与服务器联系。除非客户端请求中包含阻止提供已缓存或者未验证资源的首部。一旦过期,缓存必须和服务器核对。一旦修改过,就要获取一份新的副本。 - 过期日期和使用期
cache-control:max-age 是用的使用期。只这个副本可用使用多久 Cache-Control: max-age=484200
expires 用的是绝对时间,到这个时间之前,都可以用这个副本。Expires: Fri, 05 Jul 2002, 05:00:00 GMT - 服务器再验证
缓存文档过期不意味着服务器文档已改变,只意味着到了要核对的时间了。这种情况称为服务器再验证。
如果再验证时发生了变化,缓存会获取一份新的文档副本,并将其存储在旧文档位置,然后将其发送给客户端。
如果没有发生变化,缓存只需要获取新的首部,包括一个新的过期日期,并对缓存中首部进行更新。
条件法进行再验证
if-modified-since:Date再验证
if-none-match:实体标签再验证
有些情况仅使用最后修改日期进行再验证是不够的。
1.有些数据会周期性重写,实际包含数据一样。数据没变,但是修改日期变了
2.有些可能做了修改,但是修改不重要
3.有些服务器无法准确判断其页面的最后修改日期
4.有些服务器文档在亚秒之间发送变化(如实时监视器)。对这些服务器来说,秒为粒度的修改日期不够用。
为了解决这个问题,HTTP允许用户对被称为实体标签(ETag)的版本标识符进行比较。实体标签是附加到文档上的任意标签。他们可能包含了文档的序列号或者版本名。或者是文档内容校验和其他指纹信息。强弱验证器
有时,服务器希望文档的一些非实质性更改不要使所有的已缓存副本都失效。HTTP/1.1支持弱验证器。服务器会使用"W/"来标识弱验证器。
控制缓存的能力
cache-control:no-store
cache-control:no-cache
cache-control:must-revalidate
cache-control:max-age
附加Expires日期到响应中
不附加过期信息,缓存自己确定自己的过期日期
no-store和no-cahce首部可以防止缓存提供未经证实的已缓存的对象。标识未no-store的响应会禁止缓存对响应进行复制。缓存通常像非缓存代理服务器一样,向客户端转发一条no-store响应,然后删掉对象。
no-cache的响应其实是可以存储在本地缓存区中。只是与原始服务器进行新鲜度再验证前,缓存不能将其提供给客户端用。
max-age认为此文档处于新鲜度的秒数。expires和max-age类似,只不过expires使用绝对日期。建议使用max-age,因为很多服务器时钟不同步。
可以配置缓存,使其提供一些陈旧(过期)的对象,以提高性能。如果原始服务器希望缓存严格遵守过期信息,可以在原始响应中附加一个cache-control:must-revalidate首部。
must-revalidate告诉缓存,在事先没有和原始服务器进行再验证情况下,不能提供这个对象的副本。缓存仍然可以随意提供新鲜的副本。如果缓存进行must-revalidate新鲜度检查时,原始服务器不可用,缓存就必须返回一条504Gateway Timeout错误。
理解
I believe that must-revalidate means "once the cache expires, refuse to return stale responses to the user even if they say that they are acceptable". Whereas no-cache implies must-revlidateplus the fact the response becomes stale right away.
If a response is cacheable for 10 seconds, then must-revalidate kicks in after 10 seconds, whereas no-cache implies must-revalidate after 0 seconds.
客户端新鲜度
网关、隧道和中继
网关
单个应用无法处理所有这些能想到的资源。为了解决这个问题,就提出了网关(gateway)的概念。网关可以作为某种翻译器使用,它抽象出一种能够到达资源的方法。网关是资源和应用程序之间的粘合剂。应用程序可以(通过HTTP或其他已定义的接口)请求网关处理某条请求,网关可以提供一条响应。网关可以向数据块发送查询语句,或者生成动态内容,就像一个门一样:进去一条请求,出来一个响应。
协议网关
将HTTP流量导向网关时所使用的方式与将流量导向代理的方式相同。最常见的方式是,显式的配置浏览器使用网关,对流量进行透明的拦截,或者将网关配置为替代者(反向代理)。
资源网关
协议网关时网络连接客户端和服务器的网关。但最常见的网关时应用服务器,会将目标服务器与网关结合在一个服务器中实现。应用程序服务器是服务器网关,与客户端通过HTTP通信,并与服务器端的应用程序相连。
- 收到客户端A的请求,根据URI将其通过API发送到一个数码相机应用程序。将得到的图片绑定到一条HTTP响应报文中,再回传给客户端,在客户端的浏览器中显示。
- 客户端B的URI请求是一个电子商务应用程序。客户端B的请求是通过服务器网关API发送给电子商务软件的,结果会被回送给浏览器。电子商务软件与客户端进行交互,引导用户通过一系列HTML页面来完成购物。
第一个流行的应用程序网关API就是通用网关接口(Common Gateway Interface,CGI)。CGI是一个标准接口集,Web服务器可以用它来装载程序以响应对特定URL的HTTP请求,并收集程序的输出数据,将其放在HTTP响应中回送。
具体实现方式:请求需要使用网关资源时,服务器会请辅助应用程序来处理请求。服务器会将辅助请求数据所需的数据给它。通常就是整条请求,或者用户想在数据库上运行的请求之类的东西。
然后,它会向服务器返回一条响应或响应数据,服务器则会将其转发给客户端。服务器和网关是相互独立的应用程序,因此他们的责任分的很清楚。这个简单的协议(输入请求,转交,响应)就是最古老、最常用的服务器扩展接口CGI的本质。
CGI
CGI是第一个可能仍然得到广泛应用的服务器扩展。在Web上广泛用于动态HTML、信用卡处理以及数据块查询等任务。
CGI应用程序是独立于服务器的,所以,几乎可以用任意语言来实现。CGI很简单,几乎所有的HTTP服务都支持它。
CGI的处理对用户来说是不可见的。从客户端的角度来看,就像发起一个普通请求语言。它完全不清楚服务器和CGI程序直接的转接过程。URL中出现cgi和可能出现的"?"是客户端发现使用了CGI应用程序的唯一线索。
CGI看起来很棒,它在服务器和众多的资源类型之间提供了一种简单的、函数形式的粘合方式,用来处理各种需要的转换。这个接口还能很好的保护服务器,防止一些糟糕的扩展对服务器造成的破坏(如果扩展与服务器直接相连,造成的错误可能会引发服务器崩溃)。
但是,这种分离会造成性能的耗费。为每条CGI请求引发一个新进程的开销是很高的。加重服务器资源的负担。为了解决这个问题,人们开发了一种新型的CGI-并将其恰当的称为快速CGI。这个接口模拟了CGI,但它是作为持久守护进程运行的,消除了为每个请求建立或拆除新进程所带来的性能损耗。
CGI学习(扩展)
最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件。自己不能做,外包给别人吧,但是要与第三方做个约定,我给你什么,然后你给我什么,把请求参数发送给你,然后我接收你的处理结果给客户端。那这个约定就是 common gateway interface,简称CGI。这个协议可以用vb,c,php,python 来实现。CGI只是接口协议,根本不是什么语言。下面图可以看到流程。
WEB服务器将根据CGI程序的类型决定数据向CGI程序的传送方式,一般来讲是通过标准输入/输出流和环境变量来与CGI程序间传递数据。 如下图所示:
具体详细的CGI环境变量和post与GET方法区别,可参考这篇文章 。
服务器扩展API
CGI协议为外部翻译器与现有的HTTP服务器提供了一种简洁的接口方式,但如果想要改变服务器自身的行为,或者只是想尽可能提升能从服务器上获得的性能?服务器开发者为这两种需求提供了机制服务器扩展API,为Web开发者提供了强大的接口,以便他们将自己的模块与HTTP服务器直接相连。扩展API允许程序员将自己的代码嫁接到服务器上,或者用自己的代码将服务器的一个组件完整的替换出来。
大多数流行的服务器都会为开发者提供一个或者多个扩展API。这些扩展通常都会绑定在服务器自身的结构上,所以,大多数都是为某种服务器类型特有的。
应用程序接口和Web服务
随着Web应用程序提供的服务类型越来越多,有一点变得越来越清晰:HTTP可以作为一种连接应用程序的基础软件来使用。在将应用程序连接起来的过程中,一个更为棘手的问题是在两个应用程序之间进行协议接口的协商,以便这些应用程序可以进行数据交换-这通常是针对应用程序的个案进行的。
应用程序之间要配合工作,所要交互的信息比HTTP首部所能表达的信息要复杂的多。因此因特网委员会开发了一组允许Web应用程序相互通信的标准和协议。
隧道
Web隧道允许用户通过HTTP连接发送非HTTP流量,这样就可以在HTTP上捎带其他协议数据了。使用Web隧道最常见的原因就是要在HTTP连接中嵌入非HTTP流量,这样,这类流量就可以穿过只允许Web流量通过的防火墙了。
- 用connect建立HTTP隧道
Web隧道是用HTTP的connect方法建立起来的。connect方法并不是HTTP/1.1核心规范的一部分,但却是得到广泛应用的扩展。connect方法请求隧道网关创建一条到达任意目的服务器和端口的TCP连接,并对客户端和服务器之间的后继数据进行盲转发。
客户端识别与cookie机制
个性化接触
HTTP最初是一个匿名、无状态的请求、响应协议。服务器处理来自客户端的请求,然后向客户端回送一条响应。Web服务器几乎没有什么信息可以用来判定时哪个用户发送的请求,业无法记录来访用户的请求序列。
现代的Web站点希望能够提供个性化的接触。它们希望对另一端的用户有更多的了解,并且能在用户浏览页面时对其进行追踪。
客户端ip地址
使用客户端ip地址识别用户有很多缺点:
- 客户端ip地址描述的是机器,而不是用户。
- 因特网服务商提供动态分配ip,用户每登陆一次,都是一个新地址。
- 为了提高安全性,并对稀缺资源进行管理,很多用户都是通过网络地址转换(Network Adress Translation,NAT)防火墙来浏览网络内容的。这些NAT设备隐藏了防火墙后面实际的客户端地址。将实际的客户端IP地址转换成一个共享的防火墙IP地址(和不同的端口号)。
- HTTP代理和网关通常会打开一些新的、到原始服务器的TCP连接。Web服务器看到的将是代理服务器的IP地址,而不是客户端的。有些代理为了绕过这个问题会添加特殊的Client-IP或X-Forwarded-For扩展首部来保存原始地址。但并不是所有代理都支持这种行为。
用户登陆
Web服务器无需被动的根据用户IP来猜测身份,可以要求通过用户名密码来显式的询问用户是谁。
为了是Web站点的登陆更加简便,HTTP中包含了一种内建机制,可以用WWW-Authenticate首部和Authorization首部向Web站点传送用户的相关信息。一旦登陆,浏览器就可以不断的在每条发往这个站点的请求中发送这个登陆信息了。
- 客户端对站点www.joes-hardware.com 发起请求。
- 站点并不知道用户身份,服务器返回401 login required HTTP状态码。并添加到WWW-Authentication首部,要求用户登陆。这样浏览器就会弹出一个登陆对话框。
- 只要用户输入了用户名和密码(对其身份进行了完整性检查),浏览器就会重复原来的请求。这次它会加一个Authentication首部,说明用户名和密码并对用户名和密码加密,防止有意无意的窥探者。
- 今后的请求要使用用户名和密码时,浏览器会自动将存储下来的值发送出去,甚至在站点没有要求发送时都会发送。
cookie
大多数缓存和浏览器都不允许对任何cookie的内容进行缓存。
cookie的类型
可以笼统的将cookie分成两类:会话cookie和持久cookie。会话cookie是一种历史cookie,它记录用户访问站点时的设置和偏好。用户退出浏览器是,会话cookie就会被删除。持久cookie生存时间长一点。他们存储在硬盘上,浏览器退出,计算机重启时他们如仍然存在。通常持久性cookie维护某个用户会周期性访问的站点的配置文件或登录名。
会话cookie和持久cookie之间唯一的区别就是它们的过期时间。
cookie是任何工作
cookie就像是服务器给用户贴的”嗨,我叫"的贴纸一样。用户访问一个Web站点时,站点就可以读取服务器贴在用户身上所有的贴纸。
用户首次访问Web站点时,Web服务器对用户一无所知。Web服务器希望这个用户会再回来,所以想给这个用户拍上一个独有的cookie,这样以后就能识别这个用户了。cookie包含了一个有name=value这样的信息构成的任意列表,并通过Set-Cookie或Set-Cookie2 HTTP响应(扩展)首部将其贴到用户身上。
cookie罐:客户端状态
cookie的基本思想就是让浏览器积累一组服务器特有的信息,每次访问服务器时都将这些信息提供给它。浏览器负责存储cookie信息,所以此系统被称为客户端侧状态(client-side state)。cookie规范的正式名称为HTTP状态管理机制(HTTP state management mechanism)。
不同站点使用不同cookie
浏览器内部有成千上百个cookie,但浏览器不会将每个cookie都发送给所有的站点。通常它只向每个站点发送2-3个cookie。原因如下:
- 对所有这些cookie字节进行传输会严重降低性能。浏览器实际传输的cookie字节数要比实际内容节数多
- cookie中包含的是服务器特有的名字和值对,所以对大部分站点来说,大多数cookie都只是无法识别的无用数据。
- 将所有cookie发送给所有站点会引发潜在的隐私问题,那些你并不信任的站点也会获得你只想发给其他站点的信息。
总之,浏览器只向服务器发送服务器产生的cookie。joes-hardware.com产生的cookie会被发送到joes-hardware.com,不会发送到bobs-books.com。
- cookie的域属性
产生cookie的服务器可以向Set——Cookie响应首部添加一个Domain属性来控制哪些站点可以看到那个cookie。
Set-cookie: user="mary17"; domain="airtravelbargains.com";- cookie路径属性
cookie规范甚至允许用户将cookie与部分Web站点关联起来。可以通过path属性来实现这一功能,在这个属性列出的URL路径前缀下所有的cookie都是有效的。
例如,某个Web服务器是由两个组织共享的。每个组织有独立的cookie。站点www.airtravelbargains.com 可能将部分Web站点用于汽车租赁,例如 www.airtravelbargains.com/autos/ 用一个独立的cookie来记录用户喜欢的汽车尺寸。
Set-cookie: pref=compact; domain="airtravelbargains.com"; path=/autos/
如果用户访问 www.airtravelbargains.com/autos/specials.html 就会获得这个cookie:
cookie=“mary17"
但是如果访问www.airtravelbargains.com/autos/cheapo/index.html 就会获得两个cookie:
cookie:user="mary17"
cookie: pref=compact
cookie成分
Set-Cookie: name=value [; expires=data] [; path=path] [; domain] [; secure]
Cookie: name1=value1 [; name2=value2] ...