HTTP是一种获取资源(如HTML文档)的协议。它是Web上任何数据交换的基础,它是客户机-服务器协议,这意味着请求是由接收方(通常是Web浏览器)发起的。从获取的不同子文档(例如,文本、布局描述、图像、视频、脚本等)中重构完整的文档。
客户机和服务器通过交换单独的消息(与数据流相反)进行通信。客户端(通常是Web浏览器)发送的消息称为请求,服务器作为应答发送的消息称为响应。
HTTP是在20世纪90年代早期设计的,是一种可扩展的协议,随着时间的推移而发展。它是通过TCP或tls加密的TCP连接发送的应用层协议,尽管理论上可以使用任何可靠的传输协议。由于其可扩展性,它不仅用于获取超文本文档,还用于获取图像和视频,或者像使用HTML表单结果一样将内容发布到服务器。还可以使用HTTP获取文档的部分内容,以便根据需要更新Web页面。
基于http的系统组件
HTTP是一个客户机-服务器协议:请求由一个实体,即用户代理(或代表它的代理)发送。大多数情况下,用户代理是一个Web浏览器,但它也可以是任何东西,例如,一个抓取Web以填充和维护搜索引擎索引的机器人。
每个单独的请求都被发送到服务器,服务器处理请求并提供一个称为响应的答案。在客户机和服务器之间有许多实体,统称为代理,它们执行不同的操作,例如充当网关或缓存。
客户端:用户代理
用户代理是代表用户的任何工具。这个角色主要由Web浏览器执行,但也可以由工程师和Web开发人员用来调试其应用程序的程序执行。
浏览器始终是发起请求的实体。它从来不是服务器(尽管多年来已经添加了一些机制来模拟服务器发起的消息)。
为了显示Web页面,浏览器发送一个原始请求来获取表示该页面的HTML文档。然后,它解析该文件,根据执行脚本、要显示的布局信息(CSS)和页面中包含的子资源(通常是图像和视频)发出额外的请求。然后,Web浏览器将这些资源组合起来,以呈现完整的文档,即Web页面。浏览器执行的脚本可以在后面的阶段获取更多的资源,浏览器相应地更新Web页面。
网页是一个超文本文档。这意味着显示内容的某些部分是链接,可以激活这些链接(通常通过单击鼠标)来获取新的Web页面,从而允许用户指导其用户代理并在Web中导航。浏览器将这些指示转换为HTTP请求,并进一步解释HTTP响应以向用户提供清晰的响应。
Web服务器
在通信通道的另一端是服务器,它根据客户机的请求提供文档。虽然服务器虚拟上表现的好像只像一台机器,但它实际上可能是一组共享负载(负载平衡)的服务器或其他软件(如缓存、数据库服务器或电子商务服务器)的集合,它们全部或部分地按需生成文档。
服务器不一定是一台机器,但可以在同一台机器上托管多个服务器软件实例。使用HTTP/1.1和Host头,它们甚至可以共享相同的IP地址。
代理
在Web浏览器和服务器之间,许多计算机和机器转发HTTP消息。由于Web栈的分层结构,其中大多数操作在传输、网络或物理层,在HTTP层变得透明,并可能对性能产生重大影响。在应用层操作的那些通常称为代理。它们可以是透明的,在不以任何方式更改请求的情况下转发它们收到的请求,也可以是不透明的,在这种情况下,它们将在将请求传递给服务器之前以某种方式更改请求。代理可以执行许多功能:
- 缓存(缓存可以是公共的,也可以是私有的,就像浏览器缓存一样)
- 过滤(如防病毒扫描或家长控制)
- 负载平衡(允许多个服务器处理不同的请求)
- 身份验证(控制对不同资源的访问)
- 日志记录(允许存储历史信息)
HTTP的基本方面
HTTP很简单
HTTP通常被设计为简单和可读的,即使在HTTP/2中通过将HTTP消息封装到帧中引入了额外的复杂性。HTTP消息可以被人类阅读和理解,为开发人员提供了更容易的测试,并降低了新手的复杂性。
HTTP可扩展
HTTP headers在HTTP/1.0中引入,使该协议易于扩展和试验。甚至可以通过客户端和服务器之间关于新 header 语义的简单协议引入新功能。
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引入了流水线(这被证明是很难实现的)和持久连接:底层的TCP连接可以使用connection头部分控制。HTTP/2更进一步,在单个连接上复用消息,帮助保持连接温暖和更高效。
设计一个更适合HTTP的更好的传输协议的实验正在进行中。例如,Google正在试验基于UDP的QUIC,以提供更可靠和高效的传输协议。
HTTP可以控制什么
随着时间的推移,HTTP的这种可扩展特性允许了Web的更多控制和功能。缓存 和 身份验证 方法是HTTP历史早期处理的函数。相比之下,放宽 源约束 (origin constraint) 的功能直到2010年代才增加。
以下是HTTP可控制的常见特性列表:
-
缓存: 如何缓存文档可以由HTTP控制。服务器可以指示代理和客户端缓存什么内容以及缓存多长时间。客户端可以指示中间缓存代理忽略存储的文档。
-
放松源约束: 为了防止窥探和其他隐私侵犯,Web浏览器强制网站之间严格隔离。只有来自相同来源的页面才能访问Web页面的所有信息。虽然这样的约束对服务器来说是一个负担,但HTTP头可以在服务器端放松这种严格的分离,允许文档成为来自不同域的信息的拼凑;这样做甚至可能是出于与安全相关的原因。
-
身份验证: 某些页面可能受到保护,因此只有特定的用户才能访问它们。基本的身份验证可以由
HTTP提供,或者使用WWW-Authenticate和类似的标头(headers),或者通过使用HTTP cookie设置特定的会话。 -
代理和隧道: 服务器或客户端通常位于内网上,并对其他计算机隐藏其真实IP地址。然后,HTTP请求通过代理越过这个网络障碍。并非所有代理都是HTTP代理。例如,
SOCKS协议在更低层级上运行。其他协议,如ftp,可以由这些代理处理。 -
会话: 使用
HTTP cookie允许您将请求与服务器状态关联起来。这将创建会话,尽管基本HTTP是无状态协议。这不仅对电子商务购物篮很有用,而且对任何允许用户配置输出的站点也很有用。
HTTP 流
当客户端希望与服务器通信时,无论是最终服务器还是中间代理,它将执行以下步骤:
-
打开TCP连接:使用TCP连接来发送一个或多个请求,并接收回答。客户端可能会打开一个新连接、复用现有的连接,或者向服务器打开多个TCP连接。
-
发送HTTP消息:HTTP消息(在HTTP/2之前)是人类可读的。使用HTTP/2,这些简单的消息被封装在帧中,使它们无法直接读取,但原理保持不变。例如:
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
- 读取服务器发送的响应,例如:
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>… (这里是请求网页的29769字节)
- 关闭或复用连接以进行进一步的请求。
如果启用了HTTP流水线(pipelining),可以在不等待第一个响应完全接收的情况下发送多个请求。在现有网络中,HTTP流水线(pipelining)实现起来较为困难,旧的软件部分与现代版本共存。在HTTP/2中,通过在帧内更健壮地复用请求,取代了HTTP流水线。
HTTP消息
HTTP消息,在HTTP/1.1和更早的版本中定义,是人类可读的。在HTTP/2中,这些消息被嵌入到一个二进制结构中,一个帧,允许诸如压缩报头和多路复用之类的优化。即使在这个版本的HTTP中只发送了原始HTTP消息的一部分,每个消息的语义也不会改变,客户端(在虚拟上)重新构造原始HTTP/1.1请求。因此,在HTTP/1.1格式的对应形式下,理解HTTP/2消息是有用的。
HTTP消息有两种类型:请求和响应,每一种都有自己的格式。
请求
HTTP 请求 示例:
请求 由以下元素组成:
- HTTP方法,通常是动词(如
GET、POST)或名词(如OPTIONS或HEAD),用于定义客户端想要执行的操作。通常,客户端希望获取资源(使用GET)或发布 HTML表单 的值(使用post),尽管在其他情况下可能需要更多操作。 - 要获取的资源的路径: 资源的
URL是从上下文中明显的元素中剥离出来的,例如没有协议(http://),、域(此处为developer.mozilla.org)或TCP端口(此处为80)。 - HTTP协议的版本(
version)。 - 为服务器传递附加信息的可选标头(
headers)。 - 正文(
body), 对于某些方法(如POST),类似于响应中的方法,其中包含发送的资源。
响应
一个响应的示例:
响应 由以下元素组成:
- 它们遵循的HTTP协议的版本。
- 状态码,指示请求是否成功,以及为什么。
- 状态消息,状态码的非权威性简短描述。
- HTTP头部,类似于请求中的头部。
- (可选)包含获取的资源的正文。
基于 HTTP 的 API
基于 HTTP 最常用的 API 是 Fetch API,它可以用来从 JavaScript 中发起 HTTP 请求。Fetch API 取代了早期的 XMLHttpRequest API。
另一个 API 是 服务器发送事件(Server-Sent Events),它是一种单向服务,允许服务器使用 HTTP 作为传输机制向客户端发送事件。使用 EventSource 接口,客户端打开一个连接并建立 事件处理器(event handlers)。客户端浏览器自动将到达 HTTP 流上的消息转换为适当的 Event 对象,然后将它们传递给已经为 事件的类型(events' type) 注册的事件处理器,如果没有注册类型特定的事件处理器,则传递给 onmessage 事件处理器。
结论
HTTP 是一个可扩展、易于使用的协议。其客户端-服务器结构,结合了添加头部的能力,使得HTTP 能够随着 Web 扩展功能的发展而进步。
虽然 HTTP/2 通过在帧中嵌入 HTTP 消息以提高性能增加了一些复杂性,但自 HTTP/1.0 以来消息的基本结构保持不变。会话流程保持简单,使得可以使用简单的HTTP消息监控器进行调查和调试。