HTTP 概述

HTTP 是一种用于获取 HTML 文档等资源的协议。它是 Web 上任何数据交换的基础,并且是一种客户端-服务器协议,这意味着请求由接收方(通常是 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 设计于 20 世纪 90 年代初,是一种可扩展协议,并随着时间的推移而发展。它是一种应用层协议,通过 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 不要求底层传输协议是面向连接的;它只要求它是可靠的,或者不丢失消息(至少,在这种情况下会显示错误)。在 Internet 上最常见的两种传输协议中,TCP 是可靠的,UDP 不是。因此,HTTP 依赖于面向连接的 TCP 标准。

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

为了弥补这个缺陷,HTTP/1.1 引入了流水线(事实证明难以实现)和持久连接:底层 TCP 连接可以使用 Connection 头部进行部分控制。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 流水线已被 HTTP/2 中更强大的帧内多路复用请求所取代。

HTTP 消息

HTTP/1.1 及更早版本中定义的 HTTP 消息是人类可读的。在 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 头部,类似于请求的头部。
  • 可选地,一个包含获取资源的 body。

基于 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 网络监视器进行调查和调试。

另见