03-HTTP

nobility 发布于 2021-08-16 03-HTTP协议 202 次阅读


HTTP

特点

  • 客户端/服务端模式,只能由客户端发送请求之后才能得到服务端响应
  • 请求时只需传送请求方法和资源路径
  • 可以传输任意类型的数据,类型由 Content-Type 字段标记
  • 默认情况下一个请求由一个连接处理,请求结束后就断开连接
  • 无记忆之前的数据,需要依赖 CookiesSession 等数据存储才能实现记忆之前的数据

URI

URI(统一资源标识符),主要包括两类:

  • URL(统一资源定位符):protocol://[username:password@]host:port/path[?query#hash]
    • protocol:使用的协议,若没有此字段顶多称为 URI 而不能称为 URL
    • username:password:登入的用户名和密码,一般也没有人会这样用,因为太不安全了
    • host:port:主机和端口,主机可以是 IP 也可以是域名,域名会通过 DNS 解析成相应的 IP
    • path:服务器软件资源目录中的路径
    • query:查询字符串,使用 key=value 的形式,多个键值对使用 & 分隔
    • hash:页面片段哈希值,前端通常当作锚点定位使用
  • URN(统一资源命名符):urn:hierarchical
    • urn:表明是一个 URN,若没有此字段顶多称为 URN 而不能称为 URL
    • hierarchical:该资源的层级关系,多个层级使用 : 分隔,比如 人:男人:张三

乱码的原因

  1. 编码和解码使用的方式不一致
  2. 使用的编码方式中没有对应的字符集,即时解码方式一致也不会出现正确的结果

URL 中的百分号编码,会对不属于 ASCII 码的字符和属于 ASCII 码但是又属于保留字的字符进行百分号编码,百分号编码就是对非 ASCII 码取 Unicode 内码再加%前缀

HTTP 报文

HTTP报文格式

常用请求方法

  • GET:作为 URL 的一部分向服务器端发送,是浏览器默认请求方式
    • 由于是 URL 的一部分,所以很容易从浏览历史记录中查到表单提交内容,不太安全
    • 由于是 URL 的一部分,所以请求体就是 URL 中的 query 部分,没有显示的请求体
    • URL 长度有限制
  • POST:用于提交表单数据或大批量的数据
    • 有自己的请求体,相对比较安全
    • 没有长度限制,所以可以提交大批量数据
  • PUT:用于更新数据资源
    • HTTP 1.1 版本不带有 PUT 方法的验证机制,所以存在一定的安全隐患,一般不使用
  • DELEAT:用于删除数据资源
    • HTTP 1.1 版本不带有 DELEAT 方法的验证机制,所以存在一定的安全隐患,一般不使用
  • HEAD:与 GET 请求相同,不同的是只获取响应头,而没有响应体,所以多作用与超链接弹探测
  • OPTIONS:用于查询接口支持的请求什么方法
  • TRACE:回显服务器收到的请求,主要用于测试和诊断,容易引起一种 XST(跨站追踪)的攻击方式,所以一般不使用
  • CONNECT:开启客户端与请求资源之间的双向沟通通道,通常用于访问代理服务器

常用响应状态码

  • 1 XX:临时响应,已经接受需要继续处理,没有响应体
  • 2 XX:成功
    • 200 OK:请求已成功
    • 202 Accepted:已接受,但未处理完成
    • 206 Partial Content:服务器成功处理了部分内容,多用于请求部分内容,即断点续传完成
  • 3 XX:重定向
    • 301 Moved Permanently:永久移动,URI 会变,浏览器会有缓存
    • 302 Found:临时移动,URI 不会变,浏览器不会有缓存
    • 304 Not Modified:文件未修改,使用本地缓存文件
  • 4 XX:客户端请求错误
    • 400 Bad Request:客户端语法错误
    • 401 Unauthorized:未授权的,请求要求客户身份认证
    • 403 Forbidden:拒绝访问,没有权限
    • 404 Not Found:资源未找到
    • 406 Non Acceptable:无可接收的媒体类型
    • 416 Range Not Satisfiable:请求部分资源内容范围错误
  • 5 XX:服务器错误
    • 500 Internal Server Error:服务器内部错误,无法完成请求
    • 502 Bad Gateway:网关或代理服务器错误,无效的请求

报文头

通用报文头

可以通用于请求报文头和响应报文头

字段名 描述
Cache-Control 缓存控制
Connection 逐跳首部,连接管理
Date 创建报文的日期时间
Pragma 报文指令
Trailer 报文末端的首部
Transfer-Encoding 指定报文主体的传输编码
Upgrade 升级为其他协议
Via 代理服务器的相关信息
Warning 错误通知
  • Connection:连接管理
    • keep-alive:网页打开请求结束后不会断开 TCP 连接,若再次发送请求还会使用该连接,HTTP 1.1 默认采用长连接
    • close:请求结束后就会断开 TCP 连接,若再次发送请求会重新建立连接,HTTP 1.0 默认采用短连接
请求报文头

只在请求头中出现

字段名 描述
Accept 优先媒体类型
Accept-Charset 优先字符集编码
Accept-Encoding 优先内容编码
Accept-Language 优先自然语言
Authorization Web 认证信息
Expect 期待服务器的特定行为
From 用户电子邮箱地址
Host 请求资源服务器地址和端口号
If-Match 比较实体标记(ETag)
If-None-Match 比较实体标记
If-Modified-Since 比较资源更新时间
If-Unmodified-Since 比较资源的更新时间
If-Range 资源未更新时发送实体 Byte 的范围请求
Max-Forwards 最大传输逐跳数
Proxy-Authorization 代理服务器要求客户端的认证信息
Range 实体的字节范围请求
Referer 对请求中 URI 的原始获取放
TE 传输编码优先级
User-Agent HTTP 客户端程序信息
  • Host:请求资源服务器地址和端口号,是直接从 URL 中截取出来的,可能是 IP 也可能是域名
  • Referer:告知服务器是从那个连接中跳过来的
  • User-Agent:客户端使用的操作系统、浏览器名称和版本信息
响应报文头

只在响应头中出现

字段名 描述
Accept-Ranges 是否接受字节范围请求
Age 推算资源创建经过时间
ETag 资源的匹配信息
Location 令客户端重定向到指定的 URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 对再次发起请求的时机要求
Server HTTP 服务器的安装信息
Vary 代理服务器缓存的管理信息
WWW-Authenticate 服务器对客户端的认证信息
实体报文头

用来描述实体

字段名 描述
Allow 资源可支持的 HTTP 方法
Content-Encoding 实体主体使用的编码方式
Content-Language 实体主体的自然语言
Content-Length 实体主体的大小,以字节为单位
Content-Location 其实是代替对应资源的 URI
Content-MD5 实体主体的报文摘要
Content-Range 实体主体的位置访问
Content-Type 实体主体的媒体类型
Expires 实体主体的过期的日期时间
Last-Modified 资源的最后修改日期时间

CORS 跨域请求

需要浏览器和服务器都支持,目前所有浏览器都支持,整个 CORS 通信过程,浏览器都是自动完成的,浏览器一旦发现 AJAX 请求跨域,就会自动附加一些头部信息,所以实现 CORS 通信的关机是服务器

简单请求

简单的说简单请求就是简单的请求方法和简单的请求头的结合,表单请求也是简单请求,表单请求在历史上是一直可以跨域请求的

  • 请求方法是:HEADGETPOST

  • 请求头只有:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:仅限三个值 application/x-www-form-urlencodedmultipart/form-datatext/plain

简单请求流程

浏览器直接发出 CORS 请求,在请求头中添加一个 Origin 字段来标明本次请求来自哪个域(协议+域名+端口)

  • 若该域不在许可范围内,服务器会返回一个正常的 HTTP 响应,这个正常的响应中没有包含 Access-Control-Allow-Origin 字段,所以浏览器就会忽略掉接收请求,就会抛出一个错误被 XMLHttpRequestonerror 回调函数捕获,这种错误是无法使用状态码来判断的,因为是一个正常的 HTTP响应
  • 若该域在许可范围内,服务器就会在响应头中添加以下三个字段
    • Access-Control-Allow-Origin:该字段是必须的,要么是 * 表示可接受任意域请求,要么与请求时的 Origin 的值相同(要注意域名和 IP 的映射关系浏览器是不知道的,所以也属于不同域,而且只能设置一个值,若想设置多个就需要在程序中实现)
    • Access-Control-Allow-Credentials:该字段可选,它的值是一个布尔值,表示是否允许浏览器发送 Cookie,默认不发送(不设置就相当于 false),只能设置为 true;若只是将 XMLHttpRequestwithCredentials 设置为 true,但是服务器不允许发送 Cookie,则浏览器不会将响应内容返回给请求者,只有两者同时为 true 才行;设置上传 Cookie 后 Access-Control-Allow-Origin 字段就不能使用 *,必须与请求网页同域,Cookie 依然遵循同源政策,也就是上传是 AJAX 请求域的 Cookie 而不是当前页面的 Cookie,当前页面也无法拿到 AJAX 请求域的Cookie
    • Access-Control-Expose-Headers:该字段可选,在 CORS 请求时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到 6 个服务器返回的基本字段,若想使用该方法拿到其他的字段(包括自定义字段)就需要在该字段指定;6 个基本字段包括:``Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma`
非简单请求

非简单请求的 CORS 请求,会在正式通信前增加一次 HTTP 查询请求,询问服务器当前页面域名是否在服务器允许跨域白名单中,以及可以使用哪些 HTTP 方法和请求头字段,只有得到肯定的答复,浏览器才会发出正式的 AJAX 请求,否则就会报错

比如:请求方法是 PUTDELETEContent-Type 字段的类型是 application/json

1. 预检请求

  • 预检请求使用的是 OPTIONS 方法
  • 主要有这几个字段来进行询问
    • Origin:标明请求来自哪个域
    • Access-Control-Request-Method:要使用的请求方法
    • Access-Control-Request-Headers:要使用哪些自定义请求头字段,多个可使用逗号分隔

2. 预检请求响应

  • 主要有对应的字段来进行响应
    • Access-Control-Allow-Origin:允许跨域的域
    • Access-Control-Allow-Methods:允许使用的请求方法,多个用逗号分隔
    • Access-Control-Allow-Headers:允许使用的自定义请求头字段,多个用逗号分隔
    • Access-Control-Allow-Credentials:是否需要发送 Cookie,与简单请求中的一致
    • Access-Control-Max-Age:指定多久之内不需要在发送预检请求,以秒为单位
  • 允许:上述的字段的值都应该大于等于(预检请求的值都包含在预检响应中)请求允许
  • 不允许:返回一个正常的 HTTP 响应,但是没有任何 CORS 相关字段或者明确表示请求不符合条件,这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被 XMLHttpRequest 对象的 onerror 回调函数捕获

3. 浏览器的正常请求和响应

一旦预检请求通过后,就都跟简单请求一样,请求头会增加一个 Origin 头信息字段,响应体会增加一个 Access-Control-Allow-Origin 字段

HTTP 缓存

  • Cache-Control:缓存控制
    • no-store:所有内容都不缓存
    • no-cache:缓存,但是缓存前会判断缓存资源是否是最新
    • no-transform:代理服务器无法修改缓存内容,只是声明性的
    • max-age=x:请求缓存缓存后 x 秒内不再发起请求,与 Expires 类似,是 HTTP1.1的属性,与 Expires 共存的情况下优先级高于 Expires
    • s-maxage=x:代理服务器请求源站点缓存后 x 秒内不再发起请求
    • max-stale=x:即使缓存已经过期了,但是在 x 秒内可以使用过期缓存
    • public:客户端和代理服务器都可以缓存
    • private:只能客户端可以缓存,只是声明性的

原理

使用 Cache-Control 字段即可启动缓存机制,详细的缓存控制在上面通用报文头中

  • max-age=x:请求缓存缓存后 x 秒内不再发起请求,与 Expires 类似,是 HTTP1.1的属性,与 Expires 共存的情况下优先级高于 Expires
  • Expires:实体主体的过期的日期时间,与缓存控制中的 max-age=x 类似,是 HTTP 1.0 的字段,与 max-age=x 共存的情况下,优先级低于 max-age=x
  • If-Modified-Since:比较资源更新时间,最短时间是以秒为单位的,由于在请求头中出现,所以是客户端告诉服务器,与响应头中的 Last-Modified 是一对
  • Last-Modified:资源的最后修改日期时间,最短时间是以秒为单位的,由于在响应头中出现,所以是服务器告诉客户端,与 If-Modified-Since 是一对
  • ETag 资源的匹配信息,由文件内容生成,解决了以秒为单位的长时间限制,与 If-None-Match 是一对
  • If-None-Match 比较实体标记,由文件内容生成,解决了以秒为单位的长时间限制,与 ETag 是一对

使用缓存的流程

  1. 开启缓存,先由过期时间起到作用,在一定时间内使用本地缓存,要注意 Expires 的优先级低于 max-age=x,也就是说共存情况下 Expires 将无效
  2. 再由资源文件的标记信息起作用,也就是说同时设置了 Last-ModifiedETagLast-Modified 将无效
  3. 若未设置 ETag 文件标记,则才会使用 Last-Modified 对比文件是否修改

由步骤可知道,缓存的问题就是服务器更新,这些更新条件都是在文件过期的情况下对比文件是否跟新,为了解决这个问题,需要采用 md 5/hash 值对静态资源文件进行唯一标识名,当服务器更新时,文件的名字就会更改,名字更改了就不会在使用缓存

浏览器操作对缓存的影响

用户操作 过期时间 文件对比
地址栏回车 有效 有效
页面连接跳转 有效 有效
新打开窗口 有效 有效
前进后退 有效 有效
F 5 刷新 无效 有效
Ctrl+F 5 强制刷新 无效 无效

内容协商

原理

请求头 描述 响应头
Accept 媒体类型 Content-Type
Accept-Charset 字符集 Content-Type
Accept-Encoding 编码 Content-Encoding
Accept-Language 自然语言 Content-Language

要知道这些字段的值,若是多个,后面的会覆盖掉前面的;为了有多个值,就需要使用 q=x 权重的概念,该权重值的范围是[0,1]之间的数字,数值越大权重越高;多个值是由逗号分隔的,为了为值增加权重的概念,权重和值使用分号进行分隔

  • Accept:客户端可接受的媒体类型,若服务端无法匹配则会返回 406 错误
    • */*:全部可接受
  • Accept-Charset:客户端接受的字符集类型
    • utf-8:也可是其他类型字符集
  • Content-Type:服务端返回的实体主体的媒体类型,是 MIME 格式的类型;使用分号分隔字符集
    • text/html:HTML 格式
    • text/plain:纯文本格式
    • text/xml:XML 格式
    • image/gif:gif 图片格式
    • image/jpeg:jpg 和 jpeg 图片格式
    • image/png:png 图片格式
    • application/xhtml+xml:XHTML 格式
    • application/xml:XML 数据格式
    • application/atom+xml:Atom XML 聚合格式
    • application/json:JSON 数据格式
    • application/pdf:pdf 格式
    • application/msword:Word 文档格式
    • application/octet-stream: 二进制流数据(如常见的文件下载)
    • application/x-www-form-urlencoded:表单默认的提交数据的格式
  • Accept-Encoding:客户端可接受的压缩方式
    • *:任意压缩格式
    • gzip:gzip 压缩格式
    • deflate:deflate 压缩格式
  • Content-Encoding:服务端返回主体内容的压缩方式
    • gzip:gzip 压缩格式
    • deflate:deflate 压缩格式
  • Accept-Language:客户端可接受的自然语音类型
    • zh-cn:中国大陆
    • zh:中文
    • en:英语
    • en-us:英语美国
  • Content-Language:服务端返回主体内容的自然语言类型
    • zh-cn:中国大陆
    • zh:中文
    • en:英语
    • en-us:英语美国

协商方式

  • 客户端驱动:客户端发起请求,服务端发送可选项列表,客户端做出选择后发送第二次请求
  • 服务端驱动:服务端检查客户端的请求头并决定提供那个版本的页面,当都不匹配时服务器会返回默认版本的页面
  • 透明协商:由缓存代理服务器来代表客户端进行协商,是非 HTTP 协议标准 M,但是效率比较高

断点续传与多线程下载

原理

  • 请求头中增加字段 Range 来指定请求资源的片段,该字段的值有以下几种形式

    • bytes=0-99:请求 0 到 99 字节内容
    • bytes=-99:请求最后 99 个字节内容
    • bytes=99-:请求从第 99 到最后的内容
    • bytes=0-29,50-99:请求从 0 到 29 和 50 到 99 字节两部分的片段
  • 响应头中增加字段 Content-Range 来指定返回资源的片段,该字段的值与 Range 类似,只不过多增加了一个总大小,格式如下

    • bytes=99-/1024:返回从 99 字节到最后的内容,总大小为 1024 字节

断点续传和多线程下载的过程

断点续传是客户端主动的分片传输,而多线程是服务端主动的分片传输,原理是一致的,这里只说明断点续传

  1. 客户端请求下载一个 1024 字节大小的文件,已经下载了 512 字节
  2. 网络中断,客户端请求断点续传,在请求头中声明本次断点续传的起点,即 Range: bytes=512-
  3. 服务端收到断点续传的请求,从文件的 512 字节部分开始传输,在响应头中声明断点续传的位置,即 Content-Range: bytes=512-/1024
  4. 并且返回的 HTTP 状态码是 206 表示断点续传成功,而不是 200

认证方式

Basic 认证方式

  1. 客户端先发送请求,服务端返回 401 状态码要求用户身份认证,同时返回一个 WWW-Authenticate 响应头信息
  2. 用户填写用户名和密码后,浏览器会自动将用户名和密码字符串使用冒号连接后,采用 base 64 编码,放在请求头的 Authenticate 字段中(该字段值的前面还需要添加 Basic 空格前缀)发送给服务器
  3. 认证通过后返回 200 状态码,失败后继续返回 401

Digest 认证方式

  1. 客户端先发送请求,服务端返回 401 状态码要求用户身份认证,同时返回一个 WWW-Authenticate 响应头信息,该信息中会包含一个临时质询码(就是一个随机的字符串)
  2. 用户填写用户名和密码后,浏览器会自动将用户名和密码字符串使用冒号连接后,使用临时质询码再 base 64 编码,放在请求头的 Authenticate 字段中(该字段值的前面还需要添加 Digest 空格前缀,和临时质询码以及必要的一些信息)发送给服务器
  3. 认证通过后返回 200 状态码,失败后继续返回 401

SSL 客户端认证方式

  1. 客户端先发送请求,服务端会要求用户进行身份认证,该认证是通过表单认证,同时服务端会要求下载客户端证书
  2. 每次登入时都会将该证书以 HTTP 报文的方式发给服务器
  3. 验证通过后,就可以领取证书内的公开密钥,之后就可以开始 HTTPS 加密通信

FormBase 认证方式

就是通过 Cookie 和 Session 的认证方式

  • Cookie:
    • 是一段文本信息,用来保存用户信息,主流浏览器对是以文本文件形式保存在本地
    • 客户端请求服务器时会自动将 Cookie 在请求头中作为一个字段发送,服务端需要在响应头中使用 Set-Cookie 设置带回的 Cookie
  • Session:
    • Session 也是用来保存用户的信息,保存在服务端,通常需要借助于 Cookie 来存储一个 SessionID 来保存用户的登入状态
    • 由于保存在服务端,通常都在内存中,必然会消耗大量的服务端资源,所以为了节省服务器资源就会将一些不活跃的 Session 删除掉
    • 虽然最常用的是使用 Cookie 来存储 SessionID,但是在用户禁用 Cookie 时会无法使用,也可以采用其他方式,比如:
      • URL 重写:在 URL 中的 query 部分增加查询 SessionID 的字段
      • 隐藏表单:使用隐藏表单保存 SessionID
加油啊!即便没有转生到异世界,也要拿出真本事!!!\(`Δ’)/
最后更新于 2021-08-16