初识 HTTP:网络通讯模型、HTTP 模型

协议分析:发展历史、HTTP/1.1 报文分析

场景分析:静态资源、登录、视频播放、文件上传、跨域

应用:XHR、Fetch、Node 标准库、Axios、体验优化

其他协议扩展:WebSocket、QUIC

# HTTP 实用指南 - 笔记

# 初识 HTTP

image-20220120100347063

# 网络通信模型

网络通信模型如下图所示,HTTP 协议位于模型顶层

网络通信模型示例

# HTTP 模型

HTTP( Hyper Text Transfer Protocol ) 模型如下图所示,是一个应用层(见上图)的协议,基于 TCP 协议,它有如下特点:

  • 支持多种文件格式的传输
  • 请求 ↔ 响应 (一个请求对应一个响应)
  • 简单可扩展(除了 HTTP 标准定义的 HTTP 头外,可由客户端和服务器自行协定新的 HTTP 头)
  • 无状态

HTTP模型示例

# 协议分析

# HTTP 协议发展历史

协议版本 简介 内容
HTTP/0.9 单行协议 请求只有 GET,响应只有 HTML 文档
HTTP/1.0 构建可扩展性 增加了 Header,有了状态码,开始支持多种文档类型
HTTP/1.1 标准化协议 链接复用,缓存,内容协商
HTTP/2 更优异的表现 二进制协议,压缩 Header,服务器推送
HTTP/3 草案 未来的协议

# 报文分析(HTTP/1.1)

  • 请求报文:
    1. 起始行(格式:请求方法 请求资源路径 HTTP 版本)
    2. 请求头
    3. 空行
    4. 请求正文
  • 响应报文:
    1. 起始行(格式:HTTP 版本 状态码 状态信息)
    2. 响应头
    3. 空行
    4. 响应正文

HTTP请求&响应示例

# HTTP Method

  • GET - 请求一个指定资源的表示形式,使用 GET 的请求应该被用于获取数据
  • POST - 用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用
  • PUT - 用请求有效载荷替换目标资源的所有当前表示
  • DELETE - 删除指定的资源
  • HEAD - 请求一个与 GET 请求的响应相同的响应,但没有响应体
  • CONNECT - 建立一个到由目标资源标识的服务器的隧道
  • OPTIONS - 用于描述目标资源的通信选项
  • TRACE - 沿着到目标资源的路径执行一个消息环回测试
  • PATCH - 用于对资源的部分修改

方法分类:

  • 安全的:(不会修改服务器数据)
    • GET HEAD OPTIONS
  • 幂等的:(同样的请求多次执行效果相同
    • GET HEAD OPTIONS PUT DELETE

# 状态码

大类划分:

  • 1xx - 指示信息,表示信息已接受,继续处理
  • 2xx - 成功,表示请求已被成功接收 / 理解 / 接受
  • 3xx - 重定向,要完成请求必须进行更进一步的操作
  • 4xx - 客户端错误,请求有语法错误或请求无法实现
  • 5xx - 服务器端错误,服务器未能实现合法的请求

常见状态码:

  • 200 OK - 客户端请求成功
  • 301 - 资源(网页等)被永久转移到其他 URL
  • 302 - 临时跳转
  • 401 Unauthorized - 请求未经授权
  • 404 Not Found - 请求资源不存在,可能是输入了错误的 URL
  • 500 - 服务器内部发生了不可预期的错误
  • 504 Gateway Timeout - 网关或者代理的服务器无法在规定的时间内获得想要的响应

# RESTful API

一种 API 设计风格;REST ( Representation State Transfer ) 有以下特点:

  • 每一个 URI 代表一种资源
  • 客户端和服务器之间,传递这种资源的某种表现层
  • 客户端通过 HTTP Method,对服务器资源进行操作,实现 “表现层状态转化”

例子

# 常用请求头

  • Accept
    • 接收类型,表示浏览器支持的 MIME 类型 (对标服务端返回的 Content-Type)
  • Content-Type
    • 客户端发送出去实体内容的类型
  • Cache-Control
    • 指定请求和响应遵循的缓存机制,如 no-cache
  • If-Modified-Since
    • 对应服务端的 Last-Modified,用来匹配看文件是否变动,只能精确到 1s 之内
  • Expires
    • 缓存控制,在这个时间内不会请求,直接使用缓存,服务端时间
  • Max-age
    • 代表资源在本地缓存多少秒,有效时间内不会请求,而是使用缓存
  • If-None-Match
    • 对应服务端的 ETag,用来匹配文件内容是否改变(非常精确)
  • Cookie
    • 有 cookie 并且同域访问时会自动带上
  • Referer
    • 该页面的来源 URL(适用于所有类型的请求,会精确到详细页面地址,CSRF 拦截常用到这个字段)
  • Origin
    • 最初的请求是从哪里发起的(只会精确到端口),Origin 比 Referer 更尊重隐私
  • User-Agent
    • 用户客户端(浏览器标识)的一些必要信息,如 UA 头部等

# 常用响应头

  • Content-Type
    • 服务端返回的实体内容的类型
  • Cache-Control
    • 指定请求和响应遵循的缓存机制,如 no-cache
  • Last-Modified
    • 请求资源的最后修改时间
  • Expires
    • 应该在什么时候认为文档已经过期,从而不再缓存它
  • Max-age
    • 客户端的本地资源应该缓存多少秒,开启了 Cache-Control 后有效
  • ETag
    • 资源的特定版本的标识符,ETags 类似于指纹
  • Set-Cookie
    • 设置和页面关联的 cookie,服务器通过这个头部把 cookie 传给客户端
  • Server
    • 服务器的一些相关信息
  • Access-Control-Allow-Origin
    • 服务器端允许的请求 Origin 头部(譬如为 *)

# 缓存控制

浏览器缓存类型示例

浏览器缓存控制示例

Cookie 是呈键值对出现的,具体键值信息如下:

  • Name=value
    • 各种 cookie 的名称和值
  • Expires=Date
    • Cookie 的有效期,缺省时 Cookie 仅在浏览器关闭之前有效。
  • Path=Path
    • 限制指定 Cookie 的发送范围的文件目录,默认为当前
  • Domain=domain
    • 限制 cookie 生效的域名,默认为创建 cookie 的服务域名
  • secure
    • 仅在 HTTPS 安全连接时,才可以发送 Cookie
  • HttpOnly
    • JavaScript 脚本无法获得 Cookie
  • SameSite=[None|Strict|Lax]
    • None 同站、跨站请求都可发送
    • Strict 仅在同站发送
    • 允许与顶级导航一起发送,并将与第三方网站发起的 GET 请求一起发

# HTTP/2

更快更稳定更简单

  • 帧(frame):HTTP/2 通信的最小单位,每个帧都包含帧头,至少也会标识出当前帧所属的数据流

image-20220120103657456

  • 消息:与逻辑请求或响应消息对应的完整的一系列帧

  • 数据流:已建立的连接内的双向字节流,可以承载―条或多条消息

image-20220120103838622

  • HTTP/2 连接都是永久的,而且仅需要每个来源一个连接

  • 流控制:阻止发送方向接收方发送大量数据的机制

  • 服务器推送

image-20220120104057173

# HTTPS

HTTPS 建立在 HTTP 之上,在 HTTP 与 TCP/IP 中间插了一层 SSL/TLS(加密层),其特点如下:

  • 经过 SSL/TLS 加密
  • 对称加密与非对称加密结合
    • 随机 key 使用非对称加密传输
    • 报文内容使用对称加密传输(随机 key)

HTTPS流程示例

image-20220120104610923

# 场景分析

# 静态资源

静态资源解决方案:
缓存 + CDN ( Content Delivery Network ) + 文件名 hash

通过用户就近性和服务器负载的判断,CDN 确保内容以一种极为高效的方式为用户的请求提供服务

CDN简单原理示例

# 登录

网站记住登录态主要靠两种方式来鉴权:

  1. Session + Cookie
  2. JWT( JSON web token )

Session+cookie流程示例

JWT流程示例

  1. SSO(Single Sign On):单点登录

image-20220120112838615

# 视频播放

视频播放的 Response 状态码一般为 206 Partial Content ,意为返回部分资源,资源的长度由 Range 和 Content-range 字段决定,类型由 Content-type 决定

视频直播协议有:

协议 描述 优点
HLS HTTP Live Streaming ,Apple 公司
基于 HTTP 协议
把一段视频流,分成一个个小的基于 HTTP 的文件来下载
跨平台
RTMP Real Time Messaging Protocol ,Adobe 公司
基于 TCP
时延低
HTTP-FLV 基于 HTTP
http+flv,将音视频数据封装成 FLV 格式,然后通过 HTTP 协议传输给客户端
时延低

# 文件上传

小文件直传,大文件分片上传,在服务器整合

文件上传流程

# 跨域解决方案

  • CORS

    • Access-Control-Allow-Origin
    • Access-Control-Expose-Headers
    • Access-Control-Max-Age
    • Access-Control-Allow-Credentials
    • Access-Control-Allow-Methods
    • Access-Control-Allow-Headers
    • Access-Control-Request-Method
    • Access-Control-Request-Headers
    • Origin
  • 代理服务器

    • 同源策略是浏览器的安全策略,不是 HTTP 的
  • Iframe

    • 不方便,现在几乎没有人用

# 应用

# AJAX:XHR

  • XMLHttpRequest
  • readyState
  • 容易造成回调地狱
readyState 状态名 含义
0 UNSENT 代理被创建,但尚未调用 open () 方法。
1 OPENED open () 方法已经被调用。
2 HEADERS_ RECEIVED send () 方法已经被调用,并且头部和状态已经可获得。
3 LOADING 下载中;responseText 属性已经包含部分数据。
4 DONE 下载操作已完成。
XHR示例 >folded
function get() {
    var xhr = new XMLHttpRequest()
    //open 后面有三个参数:
    // 规定请求的类型、URL 以及是否异步处理请求。
    //method:请求的类型;GET 或 POST
    //url:文件在服务器上的位置
    //async:true(异步)或 false(同步) 默认为 true
    xhr.open('get', '/getUser?id=xxx')
    // 发送请求到后端(服务器)
    xhr.send()
    // 当请求被发送到服务器时,我们需要执行一些基于响应的任务。
    // 每当 readyState 改变时,就会触发 onreadystatechange 事件。
    //readyState 属性存有 XMLHttpRequest 的状态信息。
    // 在 xhr 的准备状态发生改变的时候,调用该方法
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr.responseText)
        }
    }
}
function post() {
    var xhr = new XMLHttpRequest()
    //post 请求方式,接口后面不能追加参数
    xhr.open('post', '/login')
    // 如果使用 post 请求方式, 而且是以 key=value 这种形式提交的
    // 那么需要设置请求头的类型
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    xhr.send('username=admin&password=123456')
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr.responseText)
        }
    }
}

# AJAX:Fetch

  • XMLHttpRequest 的升级版
  • 使用 Promise
  • 模块化设计,Response/Request/Header 对象
  • 通过数据流处理对象,支持分块读取
Fetch示例 >folded
function postData(url, data) {
  // 默认的配置选项用 * 标出
  return fetch(url, {
    body: JSON.stringify(data), // 必须与 'Content-Type' 头匹配
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // *client, no-referrer
  })
  .then(response => response.json()) //.json () 方法将响应转换为 JSON
}
postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(data)) //data 为 `response.json ()` 返回的 JSON
  .catch(error => console.error(error))

# Node:标准库 HTTP/HTTPS

  • 默认模块,无需安装其他依赖
  • 功能有限 / 不是十分友好
HTTP/HTTPS模块示例1 >folded
const https = require('https');
https.get('https://test.com?api_key=DEMO_KEY', (resp) => {
    let data = '';
    
    // A chunk of data has been received.
    resp.on('data', (chunk) => {
        data += chunk;
    });
    
    // The whole response has been received. Print out the result.
    resp.on('end', () => {
        console.log(JSON.parse(data).explanation);
    });
}).on("error", (err) => {
    console.log("Error: " + err.message);
});
HTTP/HTTPS模块示例2 >folded
import * as http from 'http';
import * as https from 'https';
import { URL } from 'url';
/**
 * 判断 url 是否支持范围请求
 * @param url 
 */
function isSupportedRange(url: URL | string): Promise<boolean> {
    return new Promise((resolve, reject) => {
        if (typeof url === 'string') url = new URL(url);
        const options: http.RequestOptions = {
            method: 'HEAD',
            headers: {
                'Range': 'bytes=0-',
            },
        };
        let req: http.ClientRequest; // 根据 URL 协议,判断使用 http 还是 https 模块发送请求
        function callback(response: http.IncomingMessage) {
            // console.log(response.statusCode);
            // console.log(response.headers);
            // 假如在响应中存在 Accept-Ranges 首部(并且它的值不为 “none”),那么表示该服务器支持范围请求。
            if (response.statusCode === 206 || (response.headers["accept-ranges"] && response.headers["accept-ranges"] !== 'none')) resolve(true);
            resolve(false);
        }
        switch (url.protocol) {
            case 'http:': {
                req = http.request(url, options, callback);
                break;
            }
            case 'https:': {
                req = https.request(url, options, callback);
                break;
            }
            default: return void resolve(false);
        }
        req.addListener('error', (err: Error) => {
            reject(err); // 请求失败
        });
        req.end(); // refresh request stream
    });
}

# Axios

  • 支持浏览器 / Nodejs 环境
  • 丰富的拦截器
Axios示例 >folded
// 全局配置
axios.defaults.baseURL = "https://api.example.com";
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});
// 发送请求
axios({
    method: 'get',
    url: 'http://test.com',
    responseType: 'stream'
}).then(function (response) {
    response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});

# 用户体验优化

# 网络优化

网络优化

# 稳定性

  • 重试是保证稳定的有效手段、但要防止加剧恶劣情况
  • 缓存合理使用,作为最后一道防线

稳定性优化

# 其他协议

# WebSocket

  • 浏览器与服务器进行双全工通讯
  • 适用于实时性要求高的场景,比如聊天室
  • URL 使用 ws:// 或 wss:// 等开头

# QUIC

  • QUIC( Quick UDP Internet Connection ) - HTTP3 的草案之一
  • 0-RTT 建联
  • 类似 TCP 的可靠传输
  • 类似 TLS 的加密传输,支持完美前向安全
  • 用户空间的拥塞控制,最新的 BBR 算法
  • 支持 HTTP2 的基于流的多路复用,但没有 TCP 的 HOL 问题
  • 前向纠错 FEC
  • 类似 MPTCP 的 Connection migration

# 参考资料

  • 字节青训营课程
  • HTTP 权威指南
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

TagBug 微信支付

微信支付

TagBug 支付宝

支付宝