《HTTP权威指南》
第一章 HTTP 概述
HTTP 是万维网上进行通信时所使用的协议方案, HTTP 有很多应用, 但最著名的是用于 Web 浏览器和 Web 服务器之间的双工通信.
Web 客户端和服务器
Web 内容通常是存放在 Web 服务器上的. Web 服务器所使用的是 HTTP 协议, 因此通常被成为 HTTP 服务器. 这些 HTTP 服务器储存了英特网中的数据.
资源
Web 服务器是 Web 资源的宿主. 最简单的 Web 资源就是 Web 服务器文件中的静态文件, 这些文件包括 HTML, 文本, 图片等等.
媒体类型
因特网上有数千种数据类型, HTTP 仔细地位每种要通过 Web 传输的对象打上 MIME 类型 (MIME type) 的数据格式标签. Web 服务器会为所有 HTTP 对象数据附加一个 MIME 类型.
MIME 类型是一种文本标记, 表示一种主要的对象类型和特定的子类型, 中间用一条杠来分割.
- HTML 格式文本文档由
text/html
类型标记 - 普通的 ASCII 文本文档由
text/plain
类型标记 - JPEG 版本的图片为
image/jpeg
类型 - …..
URI
服务器资源名被称为 统一资源标识符, URI 就像因特网中的邮政地址一样, 在世界范围内唯一标识并定位信息资源.
URL
统一资源定位符 是资源标示符最常见的形式, URL 描述了一台指定服务器上某资源的指定位置. 可以明确说明从一个精确、固定的位置获取资源.
大部分 URL 都遵循一种标准格式, 这种格式包含 3 个部分 :
- 第一部分是方案, 说明访问资源所使用的协议类型, 比如 HTTP 协议 (http://)
- 第二部分给出服务器的因特网地址 (比如,
www.taobao.com
) - 其余的部分是 Web 服务器上某个资源 (比如, /img/images.jpg)
事务
一个 HTTP 事务由一条 (从客户端发往服务器) 的请求命令和一个 (从服务器发回客户端) 的响应结果组成. 这种通信是通过名为 HTTP 报文 (HTTP message) 的格式化数据块进行的.
方法
HTTP 支持几种不同的请求命令, 这些命令被称为 HTTP 方法 (HTTP method), 5 种常见的 HTTP 方法 :
HTTP 方法 | 描述 |
---|---|
GET | 从服务器向客户端发送命名资源 |
PUT | 将来自客户端的数据保存到一个命名的服务器资源中去 |
DELETE | 从服务器中删除命名资源 |
POST | 将客户端数据发送到一个服务器网关程序 |
HEAD | 仅发送命名资源相应中的 HTTP 首部 |
状态码
每条 HTTP 相应报文返回时都会携带一个状态码, 告知客户端请求是否成功, 或者是否采取其他动作.
HTTP 状态码 | 描述 |
---|---|
200 | OK, 文档正确返回 |
302 | Redirect(重定向), 到其他地方获取资源 |
404 | Not Found, 无法找到该资源 |
报文
HTTP 报文是由一行一行的简单字符串构成的, HTTP 报文都是纯文本, 不是二进制代码, 方便读写.
从 Web 客户端发往 Web 服务器的 HTTP 报文成为 请求报文 (request message), 从服务器发往客户端的报文成为 响应报文 (response message), 此外没有其他类型 HTTP 报文.
下面是一个简单的 HTTP 报文 :
(a) 请求报文
GET /test/hi-there.txt HTTP/1.0
---------------------------------
Accept : text/*
Accept-Language : en, fr
(b) 响应报文
HTTP/1.0 200 OK
----------------------------------
Content-type : text/plain
Content-length : 19
----------------------------------
Hi! I'm a message!
连接
简要介绍了 HTTP 报文构成之后, 我们来看下报文是如何通过传输控制协议(Transmission Control Protocol, TCP) 连接一个地方搬移到另一个地方.
TCP/IP
HTTP 是个应用层协议, HTTP 无需操心网络通信的具体细节, 它把互联网的细节都交给了通用、可靠的因特网传输协议 TCP/IP.
TCP 提供了 :
- 无差错的数据传输
- 按序传输 (数据总是按照发送的顺序到达)
- 未分段的数据流 (可以在任意时刻以任意尺寸将数据发送出去)
连接、IP地址及端口号
在 HTTP 客户端向服务器发送报文之前, 需要用网际协议 (Internet Protocol, IP) 地址和端口号在客户端和服务器之间建立一条 TCP/IP 连接.
在 TCP 中, 你需要知道服务器的 IP 地址, 以及与服务器上服务器上运行的特定软件相关的 TCP 端口号.
最初怎么获得 HTTP 服务器的 IP 地址和端口号呢? 当然是通过 URL了! 我们前面曾提到过, URL 就是资源的地址, 所以自然能够为我们提供存储资源的机器的 IP 地址。 我们来看几个 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 以及端口号 80
第二个 URL 没有使用数字形式的 IP 地址, 它使用的是文本形式的域名, 或者称为主机名 (www.netscape.com). 主机名就是 IP 地址比较人性化的别称. 可以通过一种称为 域名服务 (Domain Name Service, DNS) 的机制方便的将主机名转换为 IP 地址.
第三个 URL 没有端口号, HTTP 的 URL 中没有端口号, 可以假设默认端口为 80
下图显示了浏览器如何通过 HTTP 显示位于远端服务器中的某个简单 HTML 资源的.
步骤如下:
- 浏览器从 URL 中解析出服务器的主机名
- 浏览器将服务器的主机名转换成服务器的 IP 地址
- 浏览器将端口号 (如果有的话) 从 URL 中解析出来
- 浏览器建立一条与 Web 服务器的 TCP 连接
- 浏览器向服务器发送一条 HTTP 请求报文
- 服务器向浏览器回送一条 HTTP 响应报文
- 关闭连接, 浏览器显示文档
Web 的结构组件
在因特网上, 要与许多 Web 应用程序进行交互, 下面列出一些其他比较重要的应用程序,
-
代理
位于客户端和服务器之间的 HTTP 中间实体
-
缓存
HTTP 的仓库, 使常用页面的副本可以保存在离客户端更近的地方
-
网关
连接其他应用程序的 Web 服务器
-
隧道
对 HTTP 通信报文进行盲转发的特殊代理
-
Agent 代理
发起自动 HTTP 请求的半智能 Web 客户端
代理
HTTP 代理服务器, 是 Web 安全、应用集成以及性能优化的重要组成模块.
如图, 代理位于客户端和服务器之间, 接受所有客户端的 HTTP 请求, 并将这些请求转发给服务器, 对于用户来说, 这些应用程序就是一个代理, 代表用户访问服务器.
代理通常会转发所有 Web 流量的可信任中间节点使用, 代理还可以对请求和响应进行过滤.
缓存
Web 缓存 (Web cache) 或者代理缓存 (proxy cache) 是一种特殊的 HTTP 代理服务器, 可以将经过代理传送的常用文档赋值保存起来. 客户端从附近缓存下载文档会比远程 Web 服务器下载快得多.
网关
网关 (gateway) 是一种特殊的服务器, 作为其他服务器的中间实体使用, 通常用于将 HTTP 流量转换成其他协议, 网关接受请求时就好像自己资源的远端服务器一样. 客户端可能并不知道自己正在与一个网关进行通信.
隧道
隧道 (tunnel) 是建立起来之后, 就会再两条连接之间对原始数据进行盲转发的 HTTP 应用程序. HTTP 隧道通常用来在一条或者多条 HTTP 连接上转发非 HTTP 数据, 转发时不会窥探数据.
HTTP 隧道的一种常见用途是通过 HTTP 连接承载加密的安全套接字层 (SSL, Secure Sockets Layer) 流量, 这样 SSL 流量就可以穿过只允许 Web 流量通过的防火墙了.如图所示, HTTP/SSL 隧道收到一条 HTTP 请求, 要求建立一条到目的地址和端口的输出连接, 然后在 HTTP 信道上通过隧道传输加密的 SSL 流量, 这样就可以将其盲转发到目的服务器上去了。
Agent 代理
用户 Agent 代理是代表用户发起 HTTP 请求 HTTP 请求的客户端程序. 所有发布 Web 请求的应用程序都是 HTTP Agent 代理. 到目前为止, 我们只提过一种 HTTP Agent 代理 : Web 浏览器, 但用户 Agent 还有其他类型.
第二章 URL与资源
URL 就是因特网资源标准化名称, URL 指向每一条电子信息, 告诉你它们位于何处, 以及如何与之进行交互
浏览因特网资源
URL 是浏览器寻找信息所需的资源位置.
URI 是一类更通用的资源标识符, 由两个主要子集 URL 和 URN 构成. URL 是通过描述资源的位置来标识资源的, 而 URN 则是通过名字来识别资源的.
比如说, 你想要获取 http://www.joes-hardware.com/seasonal/index.html, URL 分以下 3 部分,
- URL 第一部分是 URL 方案 (scheme), 方案可以告诉 Web 客户端怎样访问资源, 这个例子, URL 说明要使用 HTTP 协议
- URL 第二部分 (www.joes-hardware.com) 指的是服务器的位置
- URL 第三部分 (seasonal/index.html) 是资源路径
URL 的语法
大部分 URL 都遵循通用的 URL 语法, 由 9 部分构成的通用格式上 :
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
几乎没有哪个 URL 包含所有的这些组件, URL 最重要的 3 部分是方案 (scheme)、主机 (host) 和路径 (path)
比如, 看看我们的 URL : http://www.joes-hardware.com:80/index.html, 方案是 http, 主机名为 www.joes-hardware.com, 端口是 80, 路径为 /index.html
方案 — 使用什么协议
方案实际上规定定如何访问指定资源的主要标示符, 告诉负责解析 URL 的应用程序应该使用什么协议.
主机与端口
主机组件标识了因特网上能够访问资源的宿主主机, 可以用主机名 (www.joes-hardware.com), 或者 IP 地址来表示主机名.
端口组件标识了服务器正在监听的网络端口.
用户名和密码
很多服务器要求输入用户名和密码才允许用户访问数据, FTP 就是这样一个常见的实例,
ftp://ftp.prep.ai.mit.edu/pub/gnu
ftp://anonymous@ftp.prep.ai.mit.edu/pub/gnu
ftp://anonymous:my_password@ftp.prep.ai.mit.edu/pub/gnu
ftp://joe:joespasswd@ftp.prep.ai.mit.edu/pub/gnu
第一个例子没有用户和密码组件, 只有标准的方案、主机和路径.
第二个例子显示一个指定为 anonymous 的用户名.
第三个例子, 显示了用户名 (anonymous) 和 密码 (my_password), 两者用 “:” 分隔.
路径
URL 的路径组件说明了资源位于服务器的什么地方, 每个路径都有自己的参数 (param) 组件
参数
负责解析 URL 应用程序需要这些协议参数来访问资源, 否则服务器不会为请求提供服务.
参数组件, 是 URL 中的名值对列表, 由字符 “;” 分隔开. 它们为提供访问资源的附加信息, 比如 :
ftp://prep.ai.mit.edu/pub/gnu;type=d
这个例子中, 有一个参数 type=d
, 参数名为 type
, 值为 d
查询字符串
很多资源, 比如数据库服务, 都可以通过提问题或进行查询来缩小所请求的资源类型范围.
比如, 下列 URL 查询 Web 数据库网关, 查看编号为 12731 的条目是否有货
http://www.joes-hardware.com/inventory-check.cgi?item=12734
问好 (?) 右边被称为 查询 (query) 组件, 很多网关都希望查询字符串以一些列 “名/值” 对的形式出现, 名值对之间用字符 “&” 分隔 :
http://www.joes-hardware.com/inventory-check.cgi?item=12734&color=blue
这个例子中, 查询组件有两个名/值对 : item=12734 和 color=blue
片段
为了引用部分资源或资源的一个片段, URL 支持使用片段 (frag) 组件来表示一个资源内部片段, 比如 URL 可以指向 HTML 文档中一个特定的图片或小节.
片段挂在 URL 的右手边, 最前面有个字符 “#”.
第三章 HTTP 报文
报文流
HTTP 报文是在 HTTP 应用程序之间发送的数据块, 这些数据块以一些文本形式的元信息 (meta-infomation) 开头, 这些信息描述了报文的内容及含义, 后面跟着可选的数据部分.
报文流入源端服务器
HTTP 使用术语流入 (inbound) 和 流出 (outbound) 来描述事务处理 (transaction) 的方向. 报文流入源端服务器, 工作完成之后, 会流回用户的 Agent 代理中.
报文的组成部分
HTTP 报文是简单的格式化数据块, 每条报文都包含一条来自客户端的请求, 或者一条来自服务器的相应. 他们由三部分组成 : 对报文进行描述的起始行 (start line) , 包含属性的首部 (header) 块, 以及可选的、包含数据的主体部分 (body) 部分.
报文的语法
所有的 HTTP 报文都可以分为两类, 请求报文 (request message) 和响应报文 (response message) . 请求报文会向 Web 服务器请求一个动作, 响应报文会将请求结果返回客户端.
请求报文的格式
<method> <request-URL> <version>
<headers>
<entity-body>
响应报文的格式
<version> <status> <reason-phrase>
<headers>
<entity-body>
下面是对各部分的简要描述,
-
method(方法)
客户端希望服务器对资源执行的动作. 比如, GET, POST等
-
请求 URL (request-URL)
命名了所请求资源, 或者 URL 路径组件的完整 URL
-
版本 (version)
报文所使用的 HTTP 版本, 其格式看起来是这样的:
HTTP/ <major>.<minor>
主要版本号 (major) 和次要版本号 (minor) 都是整数
-
状态码 (status-code)
这三位数字描述了请求过程所发生的情况, 比如 200, 404
-
原因短语 (reason-phrase)
数字状态码的可读版本
-
首部 (header)
可以有零个或者多个首部, 每个首部包含一个名字, 后面跟着一个冒号 (:), 然后是一个可选的空格, 接着是一个值, 最后是一个 CRLF.
-
实体的主体部分
实体的主体部分包含一个由任意数据组成的数据块
起始行
所有的 HTTP 报文都以一个起始行作为开始, 请求报文的起始行说明了要做些什么, 响应报文的起始行说明发生了什么.
1. 请求行
请求报文请求服务器对资源进行一些操作. 请求报文的起始行, 或称为请求行, 包含了一个方法和一个请求的 URL, 这个方法描述了服务器应该执行的操作.
2. 响应行
响应报文的起始行, 或称为响应行, 包含了响应报文使用的 HTTP 版本、数字状态码, 以及描述操作状态的文本形式的原因短语.
3. 方法
请求的起始以方法为开始, 方法用来告诉服务器要做些什么.
方法 | 描述 |
---|---|
GET | 从服务器获取一份文档 |
HEAD | 只从服务器获取文档的首部 |
POST | 向服务器发送需要处理的数据 |
PUT | 将请求的主体部分储存在服务器中 |
TRACE | 对可能经过服务器传送到服务器上去的报文进行跟踪 |
OPTIONS | 决定可以在服务器上执行哪些方法 |
DELETE | 从服务器上删除 |
4. 状态码
方法是告诉服务器做什么事情, 状态码则是用来告诉客户端, 发生了什么事.
状态码是在每条响应报文的起始行中返回的, 回返回一个数字状态和一个可读的状态.
下面列出状态码的分类
整体状态 | 已定义范围 | 分类 |
---|---|---|
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 的资源 |
5. 原因短语
原因短语是响应起始行的最后一个组件, 它为状态码提供了文本形式的解释.
方法
方法就是客户端希望服务器对资源执行的动作.
GET
GET 是最常用的方法, 通常用于请求服务器的某个资源.
HEAD
HEAD 与 GET 方法类似, 但服务器在响应中只返回首部, 不会返回实体的主体部分. 使用 HEAD, 可以 :
- 在不获取资源的情况下了解资源的情况 (比如, 判断其类型)
- 通过查看响应中的状态码, 看某个对象是否存在
- 通过查看首部, 测试资源是否被修改
PUT
与 GET 从服务器读取文档相反, PUT 方法会向服务器写入文档.
PUT 的语义就是让服务器用请求的主体部分来创建一个由所创建的 URL 命名的新文档, 或者, 如果那个 URL 已经存在, 就用这个主体替代它.
POST
POST 方法通常是用来向服务器输入数据的, 实际上, 通常它会用来支持 HTML 的表单.
DELETE
DELETE 方法所做的事情是请求服务器删除请求 URL 所指定的资源.
状态码
HTTP 状态码被分为了 5 大类, 下面对着 5 大类进行总结
100 ~ 199 信息性状态码
200 ~ 299 成功状态码
客户端发起请求时, 这些请求通常是成功的.
300 ~ 399 重定向状态码
重定向状态码要么告知客户端使用替代位置来访问他们所感兴趣的资源, 要么就提供一个替代的响应而不是一个资源.
400 ~ 499 客户端错误状态码
有时客户端会发送一些服务器无法处理的东西, 比如格式错误的请求报文, 或者是请求一个不存在的 URL.
500 ~ 599 服务器错误状态码
有时候客户端发送请求, 服务器却出错了, 可能客户端碰上了服务器的缺陷, 或者服务器上的子元素, 比如网关资源错误.
首部
首部和方法配合工作, 共同决定了客户端和服务器能做什么事情. 首部分为 5 个主要类型 :
-
通用首部
这些是客户端和服务器都可以使用的通用首部, 可以在客户端、服务器和其他应用之间提供一些通用功能.
-
请求首部
请求首部是请求报文特有的, 它们为服务器提供了一些额外的信息.
-
响应首部
响应首部由自己的首部集, 以便为客户端提供信息.
-
实体首部
实体首部指的是用于应对实体主体部分的首部
-
拓展首部
拓展首部是非标准的首部, 由应用程序开发者创建, 但还未添加到已批准的 HTTP 规范中.