HTTP详解

229 阅读9分钟

MDN的这篇文档讲的很清晰了:HTTP | MDN (mozilla.org)

HTTP概述

HTTP 是一个客户端—服务器协议:请求由一个实体,即用户代理(user agent),或是一个可以代表它的代理方(proxy)发出。大多数情况下,这个用户代理都是一个网页浏览器,不过它也可能是任何东西,比如一个爬取网页来充实、维护搜索引擎索引的机器爬虫。

image.png

HTTP无状态,但并非无会话

HTTP 是无状态的:在同一个连接中,两个执行成功的请求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连贯的交互,比如在电商网站中使用购物车功能。尽管 HTTP 根本上来说是无状态的,但借助 HTTP Cookie 就可使用有状态的会话。利用标头的扩展性,HTTP Cookie 被加进了协议工作流程,每个请求之间就能够创建会话,让每个请求都能共享相同的上下文信息或相同的状态。

HTTP缓存

有两种不同类型的缓存:私有缓存和公有缓存

私有缓存

是绑定到特定客户端的缓存——通常是浏览器缓存。由于存储的响应不与其他客户端共享,因此私有缓存可以存储该用户的个性化响应。

另一方面,如果个性化内容存储在私有缓存以外的缓存中,那么其他用户可能能够检索到这些内容——这可能会导致无意的信息泄露。

共享缓存

共享缓存位于客户端和服务器之间,可以存储能在用户之间共享的响应。共享缓存可以进一步细分为代理缓存托管缓存

HTTPCookie

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器——如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie主要用于以下三个方面:

如用户登录状态、购物车、游戏分数或其他需要记录的信息

如用户自定义设置、主题和其他设置

如跟踪分析用户行为等

创建Cookie

服务器收到 HTTP 请求后,服务器可以在响应标头里面添加一个或多个 Set-Cookie 选项。浏览器收到响应后通常会保存下 Cookie,并将其放在 HTTP Cookie 标头内,向同一服务器发出请求时一起发送。你可以指定一个过期日期或者时间段之后,不能发送 cookie。你也可以对指定的域和路径设置额外的限制,以限制 cookie 发送的位置。关于下面提到的头部属性的详细信息,请参考 Set-Cookie 文章。

### Set-Cookie 和 Cookie 标头

服务器使用 Set-Cookie 响应头部向用户代理(一般是浏览器)发送 Cookie 信息。一个简单的 Cookie 可能像这样:

Set-Cookie: <cookie-name>=<cookie-value>

这指示服务器发送标头告知客户端存储一对 cookie:

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[页面内容]

现在,对该服务器发起的每一次新请求,浏览器都会将之前保存的 Cookie 信息通过 Cookie 请求头部再发送给服务器。

GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

定义Cookie的生命周期

Cookie 的生命周期可以通过两种方式定义:

  • 会话期Cookie会在当前的会话结束之后结束。浏览器定义了“当前会话”结束的时间,一些浏览器重启时会使用会话恢复。这可能导致会话cookie无限延长。
  • 持久性Cookie在过期时间指定的日期或者有效期指定的一段时间后被删除。

限制访问Cookie

有两种方法可以确保 Cookie 被安全发送,并且不会被意外的参与者或脚本访问:Secure 属性和 HttpOnly 属性。

跨源资源共享(CORS)

跨源资源共享(CORS)是一种基于HTTP头的机制,该机制通过允许服务器标示除了他自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。 跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预捡”请求。

image.png

HTTP 客户端提示(Clinet Hint)

客户端提示是一组HTTP请求标头字段,服务器可以主动地用它来获取关于设备、网络、用户以及用户代理指定的首选项的信息。

HTTP 消息

HTTP消息是服务器和客户端之间交换数据的方式。有两种类型的消息:

  • 请求(request)——由客户端发送用来触发一个服务器上的动作;
  • 响应(response)——来自服务器的应答。

HTTP消息有采用ASCII编码的多行文本构成。在HTTP/1.1及早期版本中,这些消息通过连接公开地发送。 在HTTP/2中,为了优化和性能方面的改进,曾经可人工阅读的消息被分到多个HTTP帧中。

HTTP/2 二进制框架机制被设计为不需要改动任何 API 或配置文件即可应用:它大体上对用户是透明的。

HTTP 请求和响应具有相似的结构,由以下部分组成:

  1. 一行起始行用于描述要执行的请求,或者是对应的状态,成功或失败。这个起始行总是单行的。
  2. 一个可选的 HTTP 标头集合指明请求或描述消息主体(body)。
  3. 一个空行指示所有关于请求的元数据已经发送完毕。
  4. 一个可选的包含请求相关数据的主体(比如 HTML 表单内容),或者响应相关的文档。主体的大小有起始行的 HTTP 头来指定。

起始行和 HTTP 消息中的 HTTP 头统称为请求头,而其有效负载被称为消息主体。

HTTP/1.x 的连接管理

在 HTTP/1.x 里有多种模型:短连接长连接和 HTTP 流水线

在早期,HTTP 使用一个简单的模型来处理这样的连接。这些连接的生命周期是短暂的:每发起一个请求时都会创建一个新的连接,并在收到应答时立即关闭。

有两个新的模型在 HTTP/1.1 诞生了。 首先是长连接模型,它会保持连接去完成多次连续的请求,减少了不断重新打开连接的时间。

然后是 HTTP 流水线模型,它还要更先进一些,多个连续的请求甚至都不用等待立即返回就可以被发送,这样就减少了耗费在网络延迟上的时间。

image.png

要注意的一个重点是 HTTP 的连接管理适用于两个连续节点之间的连接,它是逐跳(Hop-by-hop)标头,而不是端到端(End-to-end)标头。当模型用于从客户端到第一个代理服务器的连接和从代理服务器到目标服务器之间的连接时(或者任意中间代理)效果可能是不一样的。HTTP 协议头受不同连接模型的影响,比如 Connection 和 Keep-Alive,就是逐跳标头标头,它们的值是可以被中间节点修改的。

短连接

每一个 HTTP 请求都由它自己独立的连接完成;这意味着发起每一个 HTTP 请求之前都会有一次 TCP 握手,而且是连续不断的。

TCP 协议握手本身就是耗费时间的,所以 TCP 可以保持更多的热连接来适应负载。短连接破坏了 TCP 具备的能力,并且新的冷连接降低了其性能。

这是 HTTP/1.0 的默认模型(如果没有指定 Connection 协议头,或者是值被设置为 close)。而在 HTTP/1.1 中,只有当 Connection 被设置为 close 时才会用到这个模型。

长连接

短连接有两个比较大的问题:创建新连接耗费的时间尤为明显,另外 TCP 连接的性能只有在该连接被使用一段时间后(热连接)才能得到改善。为了缓解这些问题,长连接的概念便被设计出来了,甚至在 HTTP/1.1 之前。或者,这被称之为一个 keep-alive 连接。

一个长连接会保持一段时间,重复用于发送一系列请求,节省了新建 TCP 连接握手的时间,还可以利用 TCP 的性能增强能力。当然这个连接也不会一直保留着:连接在空闲一段时间后会被关闭(服务器可以使用 Keep-Alive 协议头来指定一个最小的连接保持时间)。

长连接也还是有缺点的;就算是在空闲状态,它还是会消耗服务器资源,而且在重负载时,还有可能遭受 DoS 攻击。这种场景下,可以使用非长连接,即尽快关闭那些空闲的连接,也能对性能有所提升。

在 HTTP/1.1 里,默认就是长连接的,不再需要标头(但我们还是会把它加上,万一某个时候因为某种原因要退回到 HTTP/1.0 呢)。

HTTP流水线

默认情况下,HTTP 请求是按顺序发出的。下一个请求只有在当前请求收到响应过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。

流水线是在同一条长连接上发出连续的请求,而不用等待应答返回。这样可以避免连接延迟。理论上讲,性能还会因为两个 HTTP 请求有可能被打包到一个 TCP 消息包中而得到提升。就算 HTTP 请求不断的继续,尺寸会增加,但设置 TCP 的最大分段大小(MSS)选项,仍然足够包含一系列简单的请求。

目录

[TOC]