HTTP权威指南(Web的基础篇Day3)

前言


继续

HTTP:Web 的基础

HTTP报文

报文流

HTTP 报文是在 HTTP 应用程序之间发送的数据块这些数据块以一些文本形式的元信息(meta-information)开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。这些报文在客户端、服务器和代理之间流动。术语“流入”、“流出”、“上游”及“下游”都是用来描述报文方向的。

报文流入源端服务器

HTTP 使用术语流入(inbound)和流出(outbound)来描述事务处理(transaction) 的方向。报文流入源端服务器,工作完成之后,会流回用户的 Agent 代理中。

image-20231005175544841

报文向下游流动

HTTP 报文会像河水一样流动。不管是请求报文还是响应报文,所有报文都会向下游(downstream)流动。所有报文的发送者都在接收者的上游 (upstream)。在中,对请求报文来说,代理 1 位于代理 3 的上游,但对响应报文来说,它就位于代理 3 的下游 1 。(上下游是相对的

image-20231005175714449

报文的组成部分

HTTP 报文是简单的格式化数据块。看一下图给出的例子。每条报文都包含一条来自客户端的请求,或者一条来自服务器的响应。它们由三个部分组成:对报文进行描述的起始行(start line)、包含属性的首部(header)块,以及可选的、包含 数据的主体(body)部分。(行,头,体)

image-20231005175808889

起始行和首部就是由行分隔的 ASCII 文本。每行都以一个由两个字符组成的行终止 序列作为结束,其中包括一个回车符(ASCII 码 13)和一个换行符(ASCII 码 10)。 这个行终止序列可以写做 CRLF。需要指出的是,尽管 HTTP 规范中说明应该用 CRLF 来表示行终止,但稳健的应用程序也应该接受单个换行符作为行的终止。有些老的,或不完整的 HTTP 应用程序并不总是既发送回车符,又发送换行符。 实体的主体或报文的主体(或者就称为主体)是一个可选的数据块。与起始行和首 部不同的是,主体中可以包含文本或二进制数据,也可以为空。

报文的语法

所有的 HTTP 报文都可以分为两类: 请求报文(request message)和响应报文 (response message)。请求报文会向 Web 服务器请求一个动作。响应报文会将请求的结果返回给客户端。请求和响应报文的基本报文结构相同。图中显示了获取一 张 GIF 图片所需的请求和响应报文。

image-20231005180012248

这是请求报文的格式:

1
2
3
4
<method> <request-URL> <version>
<headers>

<entity-body>

这是响应报文的格式(注意,只有起始行的语法有所不同):

1
2
3
4
<version> <status> <reason-phrase>
<headers>

<entity-body>

下面是对各部分的简要描述。

  • 方法(method)
    • 客户端希望服务器对资源执行的动作。是一个单独的词,比如 GET、HEAD 或 POST。
  • 请求URL(request-URL)
    • 命名了所请求资源,或者 URL 路径组件的完整 URL。如果直接与服务器进行对话,只要 URL 的路径组件是资源的绝对路径,通常就不会有什么问题——服务 器可以假定自己是 URL 的主机 / 端口。
  • 版本(version)
    • 报文所使用的 HTTP 版本,其格式看起来是这样的: HTTP/.
  • 状态码(status-code)
    • 这三位数字描述了请求过程中所发生的情况。每个状态码的第一位数字都用于描 述状态的一般类别(“成功”、“出错”等)。
  • 原因短语(reason-phrase)
    • 数字状态码的可读版本,包含行终止序列之前的所有文本。本章稍后提供了 HTTP 规范定义的所有状态码的原因短语示例。原因短语只对人类有意义,因 此,比如说,尽管响应行 HTTP/1.0 200 NOT OK 和 HTTP/1.0 200 OK 中原因短 语的含义不同,但同样都会被当作成功指示处理。
  • 首部(header)
    • 可以有零个或多个首部,每个首部都包含一个名字,后面跟着一个冒号(:),然 后是一个可选的空格,接着是一个值,最后是一个 CRLF。首部是由一个空行 (CRLF)结束的,表示了首部列表的结束和实体主体部分的开始。有些 HTTP 版 本,比如 HTTP/1.1,要求有效的请求或响应报文中必须包含特定的首部。
  • 实体的主体部分(entity-body)
    • 实体的主体部分包含一个由任意数据组成的数据块。并不是所有的报文都包含实 体的主体部分,有时,报文只是以一个 CRLF 结束。

image-20231005180437291

注意,一组 HTTP 首部总是应该以一个空行(仅有 CRLF)结束,甚至即使没有首部和实体的主体部分也应如此。但由于历史原因,很多客户端和服务器都在没有实体的主体部分时,(错误地)省略了最后的 CRLF。为了与这些流行但不符合规则的实现进行互通,客户端和服务器都应该接受那些没有最后那个 CRLF 的报文。

起始行

请求报文的起始行说明了要做些什么。 响应报文的起始行说明发生了什么。

请求行

请求报文请求服务器对资源进行一些操作。请求报文的起始行,或称为请求行,包 含了一个方法和一个请求 URL,这个方法描述了服务器应该执行的操作,请求 URL 描述了要对哪个资源执行这个方法。请求行中还包含 HTTP 的版本,用来告知服务 器,客户端使用的是哪种 HTTP。

响应行

响应报文承载了状态信息和操作产生的所有结果数据,将其返回给客户端。响应报 文的起始行,或称为响应行,包含了响应报文使用的 HTTP 版本、数字状态码,以 及描述操作状态的文本形式的原因短语。

方法

HTTP 规范中定义了一组常用的请求方法。比如,GET 方法负责从服务器获取一个 文档,POST 方法会向服务器发送需要处理的数据,OPTIONS 方法用于确定 Web 服务器的一般功能,或者 Web 服务器处理特定资源的能力。

image-20231006170651459

状态码

方法是用来告诉服务器做什么事情的,状态码则用来告诉客户端,发生了什么事情。

状态码是在每条响应报文的起始行中返回的。会返回一个数字状态和一个可读的状 态。数字码便于程序进行差错处理,而原因短语则更便于人们理解。 可以通过三位数字代码对不同状态码进行分类。200 到 299 之间的状态码表示成功。 300 到 399 之间的代码表示资源已经被移走了。400 到 499 之间的代码表示客户端 的请求出错了。500 到 599 之间的代码表示服务器出错了。

image-20231006170744002

原因短语

原因短语和状态码是成对出现的。原因短语是状态码的可读版本,应用程序开发者 将其传送给用户,用以说明在请求期间发生了什么情况。

版本号

版本号会以 HTTP/x.y 的形式出现在请求和响应报文的起始行中。为 HTTP 应用程序 提供了一种将自己所遵循的协议版本告知对方的方式。

首部

HTTP 首部字段向请求和响应报文中添加了一些附加信息。本质上来说,它们只是一 些名 / 值对的列表。比如,下面的首部行会向 Content-Length 首部字段赋值 19:

Content-length:19

首部分类

HTTP 规范定义了几种首部字段。应用程序也可以随意发明自己所用的首部。HTTP 首部可以分为以下几类。

  • 通用首部
    • 既可以出现在请求报文中,也可以出现在响应报文中。
  • 请求首部
    • 提供更多有关请求的信息。
  • 响应首部
    • 提供更多有关响应的信息。
  • 实体首部
    • 描述主体的长度和内容,或者资源自身。
  • 扩展首部
    • 规范中没有定义的新首部。

每个 HTTP 首部都有一种简单的语法:名字后面跟着冒号( :),然后跟上可选的空 格,再跟上字段值,最后是一个 CRLF。

image-20231006171117234

首部延续行

将长的首部行分为多行可以提高可读性,多出来的每行前面至少要有一个空格或制 表符(tab)。

1
2
3
4
5
6
例如:
HTTP/1.0 200 OK
Content-Type: image/gif
Content-Length: 8572
Server: Test Server
Version 1.0

在这个例子中,响应报文里包含了一个 Server 首部,其值被划分成了多个延续行。 该首部的完整值为 Test Server Version 1.0。

实体的主体部分

HTTP 报文的第三部分是可选的实体主体部分。就是 HTTP 要传输的内容。

HTTP 报文可以承载很多类型的数字数据:图片、视频、HTML 文档、软件应用程 序、信用卡事务、电子邮件等。

版本0.9的报文

HTTP 版本 0.9 是 HTTP 协议的早期版本。是当今 HTTP 所拥有的请求及响应报文 的鼻祖,但其协议要简单得多。

image-20231006171514631

HTTP/0.9 报文也由请求和响应组成,但请求中只包含方法和请求 URL,响应中只 包含实体。它没有版本信息(它是第一个,而且是当时唯一的版本),没有状态码或原因短语,也没有首部。

方法

安全方法

HTTP 定义了一组被称为安全方法的方法。GET 方法和 HEAD 方法都被认为是安全 的,这就意味着使用 GET 或 HEAD 方法的 HTTP 请求都不会产生什么动作。 不产生动作,在这里意味着 HTTP 请求不会在服务器上产生什么结果。

安全方法并不一定是什么动作都不执行的(实际上,这是由 Web 开发者决定的)。 使用安全方法的目的就是当使用可能引发某一动作的不安全方法时,允许 HTTP 应 用程序开发者通知用户。

GET

GET 是最常用的方法。通常用于请求服务器发送某个资源。HTTP/1.1 要求服务器 实现此方法。

image-20231006171740593

HEAD 方法与 GET 方法的行为很类似,但服务器在响应中只返回首部。不会返回实 体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查。使用 HEAD,可以:

  • 在不获取资源的情况下了解资源的情况(比如,判断其类型);
  • 通过查看响应中的状态码,看看某个对象是否存在;
  • 通过查看首部,测试资源是否被修改了。

服务器开发者必须确保返回的首部与 GET 请求所返回的首部完全相同。遵循 HTTP/1.1 规范,就必须实现 HEAD 方法。

image-20231006171902174

PUT

与 GET 从服务器读取文档相反,PUT 方法会向服务器写入文档。有些发布系统允 许用户创建 Web 页面,并用 PUT 直接将其安装到 Web 服务器上去。

image-20231006172017079

POST

POST 方法起初是用来向服务器输入数据的 3 。实际上,通常会用它来支持 HTML 的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到它要去 的地方(比如,送到一个服务器网关程序中,然后由这个程序对其进行处理)。

TRACE

客户端发起一个请求时,这个请求可能要穿过防火墙、代理、网关或其他一些应用 程序。每个中间节点都可能会修改原始的 HTTP 请求。TRACE 方法允许客户端在最终将请求发送给服务器时,看看它变成了什么样子。 TRACE 请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会 弹回一条 TRACE 响应,并在响应主体中携带它收到的原始请求报文。这样客户端就可以查看在所有中间 HTTP 应用程序组成的请求 / 响应链上,原始报文是否,以及如何被毁坏或修改过。

image-20231006172455347

TRACE 方法主要用于诊断;也就是说,用于验证请求是否如愿穿过了请求 / 响应链。它也是一种很好的工具,可以用来查看代理和其他应用程序对用户请求所产生 效果。 尽管 TRACE 可以很方便地用于诊断,但它确实也有缺点,它假定中间应用程序对 各种不同类型请求(不同的方法——GET、HEAD、POST 等)的处理是相同的。 很多 HTTP 应用程序会根据方法的不同做出不同的事情——比如,代理可能会将 POST 请求直接发送给服务器,而将 GET 请求发送给另一个 HTTP 应用程序(比如 Web 缓存)。TRACE 并不提供区分这些方法的机制。通常,中间应用程序会自行决 定对 TRACE 请求的处理方式。 TRACE 请求中不能带有实体的主体部分。TRACE 响应的实体主体部分包含了响应服务器收到的请求的精确副本。

OPTIONS

OPTIONS 方法请求 Web 服务器告知其支持的各种功能。可以询问服务器通常支持 哪些方法,或者对某些特殊资源支持哪些方法。(有些服务器可能只支持对一些特殊 类型的对象使用特定的操作)。 这为客户端应用程序提供了一种手段,使其不用实际访问那些资源就能判定访问各种资源的最优方式。

image-20231006172548945

DELETE

顾名思义,DELETE 方法所做的事情就是请服务器删除请求 URL 所指定的资源。 但是,客户端应用程序无法保证删除操作一定会被执行。因为 HTTP 规范允许服务器在不通知客户端的情况下撤销请求。

image-20231006172623491

扩展方法

HTTP 被设计成字段可扩展的,这样新的特性就不会使老的软件失效了。扩展方法 指的就是没有在 HTTP/1.1 规范中定义的方法。服务器会为它所管理的资源实现一 些 HTTP 服务,这些方法为开发者提供了一种扩展这些 HTTP 服务能力的手段。

image-20231006172701731

并不是所有的扩展方法都是在正式规范中定义的,认识到这一点很重要。如果你定 义了一个扩展方法,很可能大部分 HTTP 应用程序都无法理解。同样,你的 HTTP 应用程序也可能会遇到一些其他应用程序在用的,而它并不理解的扩展方法。 在这些情况下,最好对扩展方法宽容一些。如果能够在不破坏端到端行为的情况下 将带有未知方法的报文传递给下游服务器,代理应尝试传递这些报文。如果可能破 坏端到端行为则应以 501 Not Implemented(无法实现)状态码进行响应。最好按惯 例“对所发送的内容要求严一点,对所接收的内容宽容一些”来处理扩展方法(以 及一般的 HTTP 扩展)。

状态码

100~199——信息性状态码

HTTP/1.1 向协议中引入了信息性状态码。这些状态码相对较新,关于其复杂性和感 知价值存在一些争论,而受到限制。

image-20231006172944402

100 Continue 状态码尤其让人糊涂。它的目的是对这样的情况进行优化:HTTP 客 户端应用程序有一个实体的主体部分要发送给服务器,但希望在发送之前查看一下 服务器是否会接受这个实体。这可能会给 HTTP 程序员带来一些困扰,因此在这里 进行了比较详细(它如何与客户端、服务器和代理进行通信)的讨论。

  • 客户端与100 Continue

    如果客户端在向服务器发送一个实体,并且愿意在发送实体之前等待 100 Continue 响应,那么,客户端就要发送一个携带了值为 100 Continue 的 Expect 请求首部 (参见附录 C)。如果客户端没有发送实体,就不应该发送 100 Continue Expect 首 部,因为这样会使服务器误以为客户端要发送一个实体。 从很多方面来看,100 Continue 都是一种优化。客户端应用程序只有在避免向服务 器发送一个服务器无法处理或使用的大实体时,才应该使用 100 Continue。 由于起初对 100 Continue 状态存在一些困惑(而且以前有些实现在这里出过问题), 因此发送了值为 100 Continue 的 Expect 首部的客户端不应该永远在那儿等待服务 器发送 100 Continue 响应。超时一定时间之后,客户端应该直接将实体发送出去。

    实际上,客户端程序的实现者也应该做好应对非预期 100 Continue 响应的准备(这 很烦人,但确实如此)。有些出错的 HTTP 应用程序会不合时宜地发送这个状态码。

  • 服务器与100 Continue

    如果服务器收到了一条带有值为 100 Continue 的 Expect 首部的请求,它会用 100 Continue 响应或一条错误码来进行响应(参见表 3-9)。服务器永远也不应该向没有 发送 100 Continue 期望的客户端发送 100 Continue 状态码。但如前所述,有些出错 的服务器可能会这么做。 如果出于某种原因,服务器在有机会发送 100 Continue 响应之前就收到了部分(或 全部)的实体,就说明客户端已经决定继续发送数据了,这样,服务器就不需要 发送这个状态码了。但服务器读完请求之后,还是应该为请求发送一个最终状态码 (它可以跳过 100 Continue 状态)。 最后,如果服务器收到了带有 100 Continue 期望的请求,而且它决定在读取实体的 主体部分之前(比如,因为出错而)结束请求,就不应该仅仅是发送一条响应并关闭连接,因为这样会妨碍客户端接收响应(参见 4.7.4 节)。

  • 代理与100 Continue

    如果代理从客户端收到了一条带有 100 Continue 期望的请求,它需要做几件事 情。如果代理知道下一跳服务器(在第 6 章中讨论)是 HTTP/1.1 兼容的,或者并不知道下一跳服务器与哪个版本兼容,它都应该将 Expect 首部放在请求中向下 转发。如果它知道下一跳服务器只能与 HTTP/1.1 之前的版本兼容,就应该以 417 Expectation Failed 错误进行响应。如果代理决定代表与 HTTP/1.0 或之前版本兼容的客户端,在其请求中放入 Expect 首部和 100 Continue 值,那么,(如果它从服务器收到了 100 Continue 响应)它就不应该将 100 Continue 响应转发给客户端,因为客户端可能不知道该拿它怎么办。 代理维护一些有关下一跳服务器及其所支持的 HTTP 版本的状态信息(至少要维护 那些最近收到过请求的服务器的相关状态)是有好处的,这样它们就可以更好地处 理收到的那些带有 100 Continue 期望的请求了。

200~299——成功状态码

客户端发起请求时,这些请求通常都是成功的。服务器有一组用来表示成功的状态 码,分别对应于不同类型的请求。

image-20231006173222091

300~399——重定向状态码

重定向状态码要么告知客户端使用替代位置来访问他们所感兴趣的资源,要么就提供一个替代的响应而不是资源的内容。如果资源已被移动,可发送一个重定向状态码和一个可选的 Location 首部来告知客户端资源已被移走,以及现在可以在哪里 找到它。这样,浏览器就可以在不打扰使用者的情况下,透明地转入新的位置了。

image-20231006173335747

可以通过某些重定向状态码对资源的应用程序本地副本与源端服务器上的资源进行验证。比如,HTTP 应用程序可以查看其资源的本地副本是否仍然是最新的,或者在源端服务器上资源是否被修改过。下图中显示了一个这样的例子。客户端发送了一个特殊的 If-Modified-Since 首部,说明只读取 1997 年 10 月之后修改过的文 档。这个日期之后,此文档并未被修改过,因此,服务器回送了一个 304 状态码, 而不是文档的内容。

image-20231006173613198

后记