HTTP

Wikipedia 上 HTTP 的定义为:

超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、 协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。

所谓「HyperText」是指不限于文本传输,还用于如图片、声音等介质,可见 超媒体的定义。

HTTP 包含的内容主要是下面这几部分。

1 HTTP 基础

1.1 URL

URL 全名为统一资源定位符,是 URI(统一资源标识符)的子集,URI 还包含 URN。

作为资源定位表述,URL 并不限定于定位通过 HTTP 协议方式获取的资源,还包含如FTP/SMTP,甚至是 Jar 协议的资源表述定位。

1.1.1 语法

URL 语法如下:

<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

1.1.1 URL 编码

URL 字符集为 ASCII(7位二进制码标识)的子集,这个子集通常称为「URL安全字符集」,安全字符集排除了 ASCII 字符集中的如 %# 等 URL 语法保留字符。因为 URL 字符集为 ASCII 的子集,所以当 URL 中出现非安全字符集中的字符时,需要通过 编码 来避开这种限制性,保证 URL 的可移植性。

URL 编码机制:非安全字符包含一个百分号(%),后面跟着表示字符 ASCII 码的 十六进制数。如包含空格的 URL http://www.a.com/ a.html 转义后为http://www.a.com/%20a.html

1.2 报文

HTTP 报文是由

构成的具有特定规范格式的内容体。

报文分为 请求报文 和 响应报文。 请求报文的语法是:

<method> <request-URL> <version> 

<headers>

<entity-body>

响应报文的语法是:

<version> <status> <reason-phrase> 

<headers>

<entity-body>

a. 起始行

如上报文格式,起始行在请求报文和响应报文的格式不同,分别是 <method> <request-URL> <version><version> <status> <reason-phrase>,请求报文起始行描述 ‘使用 version 版本的 HTTP 协议通过 method 方法去 request-URL 获取资源’,响应报文描述 ‘服务器针对当次请求的响应状态 status,以及响应状态的描述短语。并同时说明此次响应的 HTTP version。’

method

作为文本(资源)传输协议,HTTP 使用 「方法」 来描述客户端希望服务端针对资源执行的操作。HTTP 默认支持7种资源操作方法,分别是:

  1. GET。从服务器获取资源。
  2. PUT。和 GET 相反,PUT 是向服务器写入资源。如客户端使用 PUT 方法向服务端发送 ‘new html’ 实体内容,服务端创建新的资源 new.html,其内容为客户端发送实体,然后向客户端返回新创建资源的 URL http://www.test.com/new.html。
  3. POST。POST 通常用来向服务端发送表单数据。
  4. HEAD。HEAD 和 GET 类似,当服务器收到 HEAD 请求时,只返回响应头。HEAD 可以用来判断资源是否存在、修改过等可以通过 headers 判断的信息。
  5. DELETE。用来删除资源。
  6. TRACE。服务端收到 TRACE 请求后,会将收到的报文放到实体中返回给客户端。TRACE 请求一般用于发起环形诊断,用于判断客户端到服务端之间请求是否被修改以及修改的内容。

version

Version 标记使用 HTTP 协议的版本,其格式为 HTTP/<major>.<minor>

status

状态码。状态码分为五大类,从100开始,600结束,每类预分配100个整数标识状态。

header

首部有五种,为:

1.3 协议与连接

HTTP 传输是建立在 TCP/IP 之上的,HTTP 要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的 TCP 连 接按序传输。TCP 收到数据流之后,会将数据流砍成被称作段的小数据块,并将段 封装在 IP 分组中,通过因特网进行传输。也就是 “HTTP over TCP over IP”。

我们平时所述的 Socket 套接字,即为使用 TCP 的 API,同样,HTTP 也基于 TCP Socket 进行连接的建立与管理。

2 Web 系统结构

2.1 Web 服务器

纯粹的 Web 服务器如 Apache HTTP Server 更偏向于资源的操作。对于后端系统,我们常说的 Web 服务器为 Tomcat,或者更轻量级的 Jetty,事实上,它们承担的是 Web 服务器 + Servlet 容器 两项功能,这类服务器除了资源的可靠获取之外,还提供了动态流程的支持(在 Servlet 中,你可以做你任何希望做的事情)。

Web 服务器核心任务是接受连接并处理请求,支持的并发连接数以及能否高效处理连接通常作为服务器性能的衡量指标。

服务器使用的 IO 模型是其是否可称为高效服务器的基础,Web 服务器通常基于 Linux 提供的五种IO模型实现。

针对于这部分,可以参考 《Netty 权威指南》,顺便了解下 Netty库。

2.2 代理

代理同样是 HTTP 服务器,科学上网的 ShadowSockets 就是在境外服务器上搭建 ss 服务器,转发请求实现。

2.3 缓存

单独的缓存服务器在当前的架构中并不常见,而是在后端服务器中通过访问集中式缓存如 Redis 这种方式实现。

2.4 网关

常见的网关是协议网关,如将 HTTP 协议转化为 Thrift 协议,还有一种为资源协议,如将 HTTP 协议转换为 MySQL 协议,直接去数据库中请求。

3 认证、安全

3.1 客户端识别

HTTP 是无状态协议,一个 HTTP 事务中只是一个请求加响应的过程。但是 HTTP 也提供了客户端识别与跟踪的支持。识别机制包含:

承载用户身份的首部包含:

cookie 的正式名称为「HTTP state management mechanism」(HTTP 状态管理机制)。是识别用户,实现回话持久化的 最好的方式。cookie 按照过期时间不同分为回话 cookie(设置了 Discard 参数但没有设置 Expires 或 Max-Age 参数) 和持久 cookie。

cookie 工作机制很简单:

  1. 浏览器请求服务器,服务器返回响应并在响应头中增加 Set-Cookie 或 Set-Cookie2 头,形式为 Set-cookie: id="34294"; domain="joes-hardware.com"
  2. 浏览器收到响应后, Set-Cookie 头声明的策略将 cookie 存储在浏览器的 cookie 库中。
  3. 对于浏览器,每次在请求之前,都会在 cookie 库中查找是否存在与请求 url 相同路径的 cookie (domain 参数),如果存在,那么在 Header 中增加 Cookie 头,将 cookie 内容随请求一并发送。

cookie 有两个不同版本规范,cookie 0 和 cookie 1,cookie 0 是网景公司制定,应用比较广泛。cookie 1 是 RFC 规范。

cookie 0 的 Set-Cookie 格式如:

Set-Cookie: name=value [; expires=date] [; path=path] [; domain=domain] [; secure]
Cookie: name1=value1 [; name2=value2] ...

包含的属性有:

3.2 客户端认证

HTTP 客户端认证流程为:

  1. 客户端请求。
  2. 服务端收到客户端请求,返回 401,并在 Header 中增加 WWW-Authenticate 头。
  3. 浏览器收到这种返回会自动弹出登陆框,要求用户输入用户名、密码。
  4. 用户输入后,浏览器会重复之前的请求,并在请求中增加 Authorization 头。
  5. 服务端收到带有 Authorization 头的请求,验证。通过后返回资源。
  6. 之后的请求,不管服务端是否需要,客户端都自动在请求中附加 Authorization,以维持登陆身份。

3.2.1 基本认证机制

基本认证中,服务器在 WWW-Authenticate Header 中的信息如 WWW-Authenticate: Basic realm=quoted-realm,其中:

浏览器在收到带有 WWW-Authenticate 头的 401 响应后,提示用户输入客户名密码,之后会将客户名、密码通过 「客户名:密码」形式组合起来,然后使用 Base64 编码,最终在 Header 中增加 Authorization: Basic base64-username-and-password 头继续请求。

基本认证缺陷:

所以这种认证方式适用于需要保护隐私但是又不是非常必要的环境中。

3.2.2 摘要认证机制

摘要认证较基本认证的改进点:

HTTP 设计者曾在 RFC 2617 中建议: “在可行的情况下应该将目前在用的所有使用 基本认证的服务,尽快地转换为摘要认证方式。”

针对第一点「明文密码」的问题,摘要认证是将密码使用摘要函数编码,然后传递摘要后的结果。由于摘要是单项函数,所以即使获取到传输的摘要结果也并不能获取密码。常见的摘要算法如 MD5,将任意长度信息转换为 128 bit 结果。

针对第二点 「重放认证头」的问题,摘要认证使用随机数的方式解决,服务端在响应中会返回客户端使用的摘要算法和随机数,客户端使用这个摘要算法和随机数计算摘要结果。由于这个摘要结果只针对当前的随机数有效,所以可以使用经常变化的随机数解决请求重放的问题。

一下是摘要认证的流程:

其中的几个关键概念:

3.3 HTTPS

HTTPS 是最安全的 HTTP 形式。

4 实体、编码、国际化

4.1 实体

实体 entity 是报文中需要传输内容的表述,实体包含 entity-body 与 entity-header,entity-header 用于描述实体类型、大小、修改时间、过期等信息。以下是两个常用的 entity-header:

其它还包含如 Content-Encoding/Content-Language/Last-Modified/Cache-Control…

4.2 编码

编码是指对实体内容的可逆转换,常见的处理方式包含压缩、加密等。下图标识服务器使用 gzip 压缩,客户端使用 gizp 解压获取编码。

其中的 Content-Encoding: gzip 为编码形式,除了 gzip 外,其它常见的编码如 zlib 格式的 deflate、Unix 文件压缩格式的 compress 等。

4.2 国际化

a) 传输内容国际化

HTTP 的实体是 二进制信息 的载体,所以可能承载任何形式媒体类型和任何语言。

几个概念:

而通常所述的编码包含上述 字符编码 和 字符集 两部分。

也就是,经过字符集编码的字符是 HTTP 最终传输的二进制数据。Content-Type 中的 charset 用来标识编码方式,如 Content-Type: text/html; charset=iso-8859-6 代表使用了 iso 编码方式。 而客户端使用 Accept-LanguageAccept-Charset 头部表示客户端能处理的语言与编码。

如下两图说明了 HTTP 实体中的字符表示与字符编码流程。

关于字符编码,可以看看这篇文章,其中作者的经历也比较有意思。

b) URI 国际化

如上 URI 部分所述,URI 是由 ASCII 的安全子集组成,ASCII 中的非安全字符采用 % + 字符级中编码值的十六进制表述,如 %HEX 。 至今 URI 对国际化没有很好的支持,仍推荐 ASCII 安全子集表述形式。

参考