HTTP 概述

HTTP 是用于获取资源(如 HTML 文档)的协议。它是网络上任何数据交换的基础,并且是一种客户端-服务器协议,这意味着请求是由接收方(通常是 Web 浏览器)发起的。完整的文档通常由诸如文本内容、布局指令、图像、视频、脚本等资源构建而成。

A single Web document composed from multiple resources from different servers.

客户端和服务器通过交换单个消息(而不是数据流)进行通信。客户端发送的消息称为请求,服务器作为答案发送的消息称为响应

HTTP as an application layer protocol, on top of TCP (transport layer) and IP (network layer) and below the presentation layer.

HTTP 设计于 1990 年代初期,是一个可扩展的协议,随着时间的推移而不断发展。它是一个应用层协议,通过TCPTLS 加密的 TCP 连接发送,尽管理论上可以使用任何可靠的传输协议。由于其可扩展性,它不仅用于获取超文本文档,还用于获取图像和视频,或将内容发布到服务器,例如使用 HTML 表单结果。HTTP 还可以用于获取文档的部分内容以按需更新网页。

基于 HTTP 的系统的组件

HTTP 是一种客户端-服务器协议:请求由一个实体(用户代理或代表它的代理)发送。大多数情况下,用户代理是 Web 浏览器,但它可以是任何东西,例如,一个爬取 Web 以填充和维护搜索引擎索引的机器人。

每个单独的请求都发送到一个服务器,该服务器处理它并提供一个称为响应的答案。在客户端和服务器之间存在许多实体,统称为代理,它们执行不同的操作并充当网关或缓存,例如。

A HTTP request from a client forwarded by several proxies to a server and a response taking the same route back to the client.

实际上,在浏览器和处理请求的服务器之间还有更多计算机:路由器、调制解调器等等。由于 Web 的分层设计,这些隐藏在网络和传输层中。HTTP 位于顶部,在应用层。尽管对于诊断网络问题很重要,但底层对于 HTTP 的描述大多无关紧要。

客户端:用户代理

用户代理是任何代表用户执行操作的工具。此角色主要由 Web 浏览器执行,但也可能由工程师和 Web 开发人员用于调试其应用程序的程序执行。

浏览器**始终**是发起请求的实体。它永远不是服务器(尽管多年来已经添加了一些机制来模拟服务器发起的消息)。

为了显示网页,浏览器会发送一个原始请求来获取表示该页面的 HTML 文档。然后它解析此文件,发出与执行脚本、显示的布局信息 (CSS) 和页面中包含的子资源(通常是图像和视频)相对应的其他请求。然后 Web 浏览器将这些资源组合在一起以呈现完整的文档,即网页。浏览器执行的脚本可以在后面的阶段获取更多资源,并且浏览器会相应地更新网页。

网页是一个超文本文档。这意味着显示内容的某些部分是链接,可以通过激活(通常是单击鼠标)来获取新网页,从而允许用户引导其用户代理并在 Web 上导航。浏览器将这些指令转换为 HTTP 请求,并进一步解释 HTTP 响应以向用户呈现清晰的响应。

Web 服务器

在通信通道的另一端是服务器,它根据客户端的请求提供文档。服务器实际上只显示为一台机器;但它实际上可能是一组共享负载的服务器(负载均衡),或其他软件(例如缓存、数据库服务器或电子商务服务器),根据需要完全或部分地生成文档。

服务器不一定是单台机器,但可以在同一台机器上托管多个服务器软件实例。使用 HTTP/1.1 和Host 标头,它们甚至可以共享相同的 IP 地址。

代理

在 Web 浏览器和服务器之间,许多计算机和机器中继 HTTP 消息。由于 Web 堆栈的分层结构,其中大多数在传输、网络或物理层运行,在 HTTP 层变得透明,并且可能对性能产生重大影响。在应用层运行的那些通常称为代理。这些可以是透明的,转发它们接收到的请求而不以任何方式更改它们,也可以是非透明的,在这种情况下,它们会在将请求传递到服务器之前以某种方式更改请求。代理可以执行许多功能

  • 缓存(缓存可以是公共的或私有的,例如浏览器缓存)
  • 过滤(如防病毒扫描或家长控制)
  • 负载均衡(允许多台服务器服务不同的请求)
  • 身份验证(控制对不同资源的访问)
  • 日志记录(允许存储历史信息)

HTTP 的基本方面

HTTP 很简单

HTTP 通常设计为简单且易于人类阅读,即使在 HTTP/2 中通过将 HTTP 消息封装到帧中引入了额外的复杂性。HTTP 消息可以由人类读取和理解,从而为开发人员提供更轻松的测试,并降低新手的复杂性。

HTTP 是可扩展的

在 HTTP/1.0 中引入的HTTP 标头 使此协议易于扩展和实验。客户端和服务器之间关于新标头语义的简单协议甚至可以引入新的功能。

HTTP 是无状态的,但不是无会话的

HTTP 是无状态的:在同一连接上连续执行的两个请求之间没有链接。这立即有可能对试图连贯地与某些页面交互的用户造成问题,例如,使用电子商务购物篮。但是,虽然 HTTP 本身的核心是无状态的,但 HTTP cookie 允许使用有状态会话。使用标头可扩展性,HTTP Cookie 被添加到工作流程中,允许在每个 HTTP 请求上创建会话以共享相同的上下文或相同的状态。

HTTP 和连接

连接在传输层控制,因此从根本上超出了 HTTP 的范围。HTTP 不需要底层传输协议是基于连接的;它只需要它是可靠的,或者不会丢失消息(至少在这种情况下会显示错误)。在互联网上两种最常见的传输协议中,TCP 是可靠的,而 UDP 不是。因此,HTTP 依赖于基于连接的 TCP 标准。

在客户端和服务器可以交换 HTTP 请求/响应对之前,它们必须建立一个 TCP 连接,此过程需要多次往返。HTTP/1.0 的默认行为是为每个 HTTP 请求/响应对打开一个单独的 TCP 连接。当在短时间内发送多个请求时,这不如共享单个 TCP 连接有效。

为了缓解此缺陷,HTTP/1.1 引入了管道(事实证明难以实现)和持久连接:可以使用Connection 标头部分控制底层 TCP 连接。HTTP/2 通过在单个连接上多路复用消息更进一步,有助于保持连接温暖和更高效。

正在进行实验以设计更适合 HTTP 的更好的传输协议。例如,Google 正在试验QUIC,它建立在 UDP 之上以提供更可靠和高效的传输协议。

HTTP 可以控制什么

HTTP 的这种可扩展性,随着时间的推移,允许对 Web 进行更多控制和功能。缓存和身份验证方法是 HTTP 历史早期处理的功能。相比之下,放宽来源约束的能力直到 2010 年代才被添加。

以下是可以使用 HTTP 控制的常见功能列表

  • 缓存:文档的缓存方式可以通过 HTTP 控制。服务器可以指示代理和客户端缓存什么以及缓存多长时间。客户端可以指示中间缓存代理忽略存储的文档。
  • 放宽来源约束:为了防止窥探和其他隐私侵犯,Web 浏览器会在网站之间实施严格的分离。只有来自相同来源的页面才能访问网页的所有信息。尽管这种约束对服务器来说是一个负担,但 HTTP 标头可以在服务器端放宽这种严格的分离,允许文档成为来自不同域的信息的拼凑而成;这样做甚至可能有安全相关的原因。
  • 身份验证:某些页面可能受到保护,以便只有特定用户才能访问它们。HTTP 可以提供基本身份验证,或者使用WWW-Authenticate 和类似标头,或者使用HTTP cookie 设置特定的会话。
  • 代理和隧道:服务器或客户端通常位于内部网中,并将其真实的 IP 地址隐藏在其他计算机中。然后 HTTP 请求通过代理跨越此网络屏障。并非所有代理都是 HTTP 代理。例如,SOCKS 协议在较低的级别运行。其他协议,如 ftp,可以由这些代理处理。
  • 会话:使用 HTTP cookie 允许您将请求与服务器的状态链接起来。这创建了会话,尽管基本的 HTTP 是无状态协议。这不仅对电子商务购物篮有用,而且对任何允许用户配置输出的站点都有用。

HTTP 流程

当客户端想要与服务器(最终服务器或中间代理)通信时,它会执行以下步骤

  1. 建立 TCP 连接:TCP 连接用于发送一个或多个请求并接收响应。客户端可以打开新的连接、重用现有连接或向服务器打开多个 TCP 连接。
  2. 发送 HTTP 消息:HTTP 消息(在 HTTP/2 之前)是人类可读的。在 HTTP/2 中,这些简单的消息被封装在帧中,使其无法直接读取,但原理保持不变。例如
    http
    GET / HTTP/1.1
    Host: developer.mozilla.org
    Accept-Language: fr
    
  3. 读取服务器发送的响应,例如
    http
    HTTP/1.1 200 OK
    Date: Sat, 09 Oct 2010 14:28:02 GMT
    Server: Apache
    Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
    ETag: "51142bc1-7449-479b075b2891b"
    Accept-Ranges: bytes
    Content-Length: 29769
    Content-Type: text/html
    
    <!DOCTYPE html>… (here come the 29769 bytes of the requested web page)
    
  4. 关闭或重用连接以进行进一步的请求。

如果启用了 HTTP 管道化,则可以在不等待第一个响应完全接收的情况下发送多个请求。HTTP 管道化已被证明难以在现有网络中实现,因为旧软件与现代版本共存。HTTP/2 中的更强大的帧内多路复用请求取代了 HTTP 管道化。

HTTP 消息

HTTP 消息,如 HTTP/1.1 及更早版本中定义的那样,是人类可读的。在 HTTP/2 中,这些消息被嵌入到二进制结构(即 *帧*)中,从而允许进行诸如压缩头部和多路复用之类的优化。即使在此版本的 HTTP 中只发送了原始 HTTP 消息的一部分,每个消息的语义也保持不变,并且客户端会(虚拟地)重建原始的 HTTP/1.1 请求。因此,以 HTTP/1.1 格式理解 HTTP/2 消息很有用。

HTTP 消息有两种类型:请求和响应,每种都有自己的格式。

请求

一个 HTTP 请求示例

Overview of a HTTP GET request with headers

请求包含以下元素

  • 一个 HTTP 方法,通常是动词,如 GETPOST,或名词,如 OPTIONSHEAD,用于定义客户端想要执行的操作。通常,客户端希望获取资源(使用 GET)或发布 HTML 表单 的值(使用 POST),但在其他情况下可能需要更多操作。
  • 要获取的资源的路径;从上下文中明显可见的元素中剥离出的资源的 URL,例如,不包括 协议 (http://)、域名(此处为 developer.mozilla.org)或 TCP 端口(此处为 80)。
  • HTTP 协议的版本。
  • 可选的 头部,用于向服务器传递其他信息。
  • 主体,对于某些方法(如 POST),类似于响应中的主体,其中包含发送的资源。

响应

一个响应示例

Overview of a '200 OK' HTTP response to a GET request including response headers.

响应包含以下元素

  • 它们遵循的 HTTP 协议的版本。
  • 一个 状态码,指示请求是否成功以及原因。
  • 状态消息,状态码的非权威性简短描述。
  • HTTP 头部,与请求中的头部类似。
  • 可选地,包含已获取资源的主体。

基于 HTTP 的 API

基于 HTTP 最常用的 API 是 Fetch API,它可以用于从 JavaScript 发出 HTTP 请求。Fetch API 替换了 XMLHttpRequest API。

另一个 API,服务器发送事件,是一种单向服务,允许服务器使用 HTTP 作为传输机制向客户端发送事件。使用 EventSource 接口,客户端打开连接并建立事件处理程序。客户端浏览器会自动将到达 HTTP 流的消息转换为相应的 Event 对象。然后,如果已知,它会将它们传递给为事件的 type 注册的事件处理程序,或者如果未建立类型特定的事件处理程序,则传递给 onmessage 事件处理程序。

结论

HTTP 是一种易于使用的可扩展协议。客户端-服务器结构,结合添加头部的能力,使 HTTP 能够随着 Web 的扩展功能而发展。

虽然 HTTP/2 通过将 HTTP 消息嵌入到帧中以提高性能而增加了一些复杂性,但消息的基本结构自 HTTP/1.0 以来一直保持不变。会话流程仍然基本,允许使用 HTTP 网络监视器 进行调查和调试。