Better

Ethan的博客,欢迎访问交流

HTTP实践

HTTP核心知识与实践总结

5层网络模型

  • 应用层(HTTP/FTP)
    • 为应用软件提供了很多服务
    • 构建与TCP协议之上
    • 屏蔽了网络传输相关细节
  • 传输层(TCP/UDP)
    • 提供可靠的端到端服务
    • 向高层屏蔽了下层数据通信的细节
  • 网络层:为数据在节点之间传输创建逻辑链路
  • 数据链路层:通信的实体间建立数据链路链接
  • 物理层:定义物理设备如何传输数据

浏览器输入URL到请求返回

WX20180618-224833@2x.png

HTTP协议发展历史

  • HTTP/0.9
    • 使用一个GET动词
    • 没有HEADER等描述数据的信息
    • 仅能传输 html 文件
    • 服务发送完毕,就关闭了TCP连接
  • HTTP/1.0
    • 增加了很多动词
    • 增加了status code和header
    • 多字符集支持,多部分发送,权限、缓存等
    • 队头阻塞(Head-of-Line Blocking ,每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接
    • 默认是短连接,即每个 HTTP 请求都要使用 TCP 协议通过三次握手和四次挥手实现
  • HTTP/1.1
    • 可以重复使用连接(keep-alive),这也为管线化方式(pipelining)打下基础。
    • 添加了 Pipeline,这允许在第一个请求的答案完全传输之前发送第二个请求这降低了通信的延迟。不幸的是因为 HTTP 是一个无状态的协议,一个体积很大的或慢 response 仍然会阻塞后面所有的请求,每条 request 无法知道哪条 response 是返回给他的,服务端只能根据顺序来返回 response,这就是队头阻塞,这导致主流浏览器上默认下该功能都是关闭状态,在 http2.0 中会解决这个问题。
    • 增加了host和其他一些命令
    • chunked 机制,分块响应
    • 引入了额外的缓存控制机制
  • HTTP2
    • 所有数据以二进制传输(之前都是以字符串)
    • 二进制帧层:允许请求和响应数据分割为更小的帧,并且它们采用二进制编码 (http1.0 基于文本格式)。多个帧之间可以乱序发送,根据帧首部的流(比如每个流都有自己的 id)表示可以重新组装。
    • Stream 流:流的概念提出是为了实现多路复用,在单个连接上实现同时进行多个业务单元数据的传输
    • 多路复用协议。可以通过同一连接发出并行请求,从而消除 HTTP/1.x 协议的约束
    • 头部压缩算法 HPACK。由于一些请求在一组请求中通常是相似的,因此这消除了传输数据的重复和开销。实际上 HTTP2 并没有改变 HTTP1.x 的语义,只是把原来 HTTP1.x 的 header 和 body 部分用 frame 重新封装了一层
    • 它允许服务器通过称为服务器推送的机制在客户端缓存中填充数据
  • HTTP/3.0 (HTTP-over-QUIC):一种基于 UDP 的低时延的互联网传输层协议

HTTP三次握手

  • 客户端发送一个申请创建连接的数据包,SYN=1(标志位,创建请求数据包),Seq=X
  • 服务端开启TCP socket端口,返回给客户端SYN=1,ACK=X+1(客户端发送的Seq+1),Seq=Y
  • 客户端再次发送ACK=Y+1,Seq=Z

SYN其实并不是等于1,而是SYN占据了第一个标志位

三次握手目的:规避网络传输当中延迟,导致服务端创建多余的连接

HTTP请求与响应是建立在TCP connection之上。

URI、URL、URN

  • URI:Uniform Resource Identifier/统一资源标志符,包含URL和URN,用来唯一标识互联网上的信息资源
  • URL:Uniform Resource Locator/统一资源定位器
    • scheme://user:pass@host:port/path?query=string#hash
    • 此类格式都叫做URL,比如ftp协议也叫做URL
  • URN:永久统一资源定位符,在资源移动之后还能被找到,目前还没有非常成熟的使用方案

HTTP报文格式

  • 起始行 + 首部 + 主体
  • 请求起始行 Verb + url + 协议及其版本
  • 响应起始行 协议及其版本 + 状态码 + 状态描述
  • 起始行和首部之间是一个换行,首部和主体之间是一个完整的空行
  • HTTP方法,用来定义对于资源的操作
  • HTTP CODE
    • 定义服务器对请求的处理结果
    • 各个区间的CODE有各自的语义
    • 一个好的HTTP服务可以通过CODE判断结果

HTTP客户端与跨域

  • 浏览器,同源限制是浏览器的安全限制,与HTTP协议无关
  • curl:参数-v显示详细信息
  • 解决跨域:cros,jsonp

CORS跨域限制

  • 简单设置Access-Control-Allow-Origin并不能完全解决跨域问题
  • 跨域时默认允许的方法只有GET/HEAD/POST,其他方法需要通过预请求进行验证
  • 跨域时默认允许的Content-Type,其他内容类型需要通过预请求进行验证
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  • 其他限制
    • 请求头限制
    • XMLHttpRequestUpload对象均没有注册任何事件监听器
    • 请求中没有使用ReadableStream对象
  • OPTIONS请求方法,用来探索服务器支持的方式,而且其实服务器数据已经方式,同样是由于浏览器的安全限制进行了屏蔽,同时报错提示。
  • 其他CORS相关的头
    • Access-Control-Allow-Headers
    • Access-Control-Allow-Methods
    • Access-Control-Max-Age:设置多少秒之内无须再次发送预请求

缓存

Cache-Control

  • 可缓存性
    • public:客户端与代理服务器都可以进行缓存
    • private:只有发起请求的浏览器可以进行缓存
    • no-cache:任何节点都不能缓存(本质其实可以缓存,但是每次都必须去服务器验证)
  • 到期
    • max-age=<seconds>
    • s-maxage=<seconds>,只有代理服务器才会生效,如果代理服务器同时设置了两者,则忽略max-age
    • max-stale:发起请求这一方主动带的头,在max-age过期后,只要在max-stale时间,则依旧可以使用过期的缓存,无需从源服务器请求新的内容,在浏览器用不到(因为在发起请求时并不会主动加上这个设置),只有在发起端是有用的,在服务端返回的内容中设置它没有作用
  • 重新验证
    • must-revalidate:如果max-age到期了,则必须从源服务端重新获取数据
    • proxy-revalidate:用在缓存服务器中
  • 其他
    • no-store:客户端和代理服务器彻底不能进行缓存
    • no-transform:主要用在代理服务器,告诉代理服务器不要去改变内容

Last-Modified和Etag的使用

  • 用途:资源验证
  • Last-Modified
    • 上次修改时间
    • 配合If-Modified-Since或者If-Unmodified-Since使用
    • 对比上次修改时间以验证资源是否需要更新
  • Etag
    • 数据签名,根据内容产生签名
    • 配合If-Match或者If-None-Match使用
    • 对比资源的签名判断是否使用缓存
  • 实践
    • Last-Modified
      • 文件是有上次修改时间这个属性的
      • 数据库资源可以考虑添加update-at字段
    • Etag
      • 服务器读取完数据后做一个签名

缓存扮演的角色

WX20180620-233254@2x.png

  • 服务端通过Set-Cookie响应头设置,Set-Cookie头可以重复
  • 下次请求会自动带上
  • 键值对保存,可以设置多个
  • cookie属性
    • max-age(秒)和expires(时间点)设置过期时间
    • 设置secure只在https的时候发送
    • 设置httpOnly无法通过document.cookie访问
    • domain属性设置cookie作用域,可以达到二级域名共享顶级域名cookie
  • session不一定需要cookie实现,比如我们可以通过JS写到header里,但cookie最常见的方式

HTTP长连接

  • 通过chrome->inpect,选择ConnectionID可以看到TCP connector的ID,如果多个请求相同,则表示复用了
  • 为什么还会有多个ID呢,因为HTTP1.1中发送请求是有先后顺序的
  • 如果要并发的话,浏览器允许你创建多个TCP connector,比如chrome限制最大可以6个。
  • 通过connection:keep-alive开启长连接,HTTP1.1中默认开启长连接,当然可以通过设置为close强制关闭,但不建议这么做
  • HTTP2信道复用,可以在一个TCP连接上并发的发送请求

数据协商

  • 请求
    • Accept
    • Accept-Encoding
    • Accept-Language
    • User-Agent
  • 返回
    • Content-Type
    • Content-Encoding
    • Content-Language
  • 其他
    • 告知浏览器不要去猜测内容:X-Content-Type-Options:nosniff
  • chrome Size字段为啥有两个值
    • 上一个值表示数据在整个传输中的实际大小,包含headers,首行信息,body
    • 下一个值表示body实际内容的大小,表示数据传输后且根据Content-Encoding解压后的数据大小
  • Content-Type也可以在请求中,因为客户端也需要往服务端发送内容,并不只是接受
    • multipart/form-data
    • application/json
    • application/x-www-url-formencoded

Redirect

  • 使用302(临时跳转)状态码配合Location响应头实现跳转,具体表现,每次都会从 old -> new
  • 301(永久跳转),第一次得到Location后,后续浏览器会直接访问新的地址,而不需要多次跳转,使用301跳转需要特别慎重,因为浏览器会将 old 永久缓存,一旦使用了,你想反悔,服务端是搞不定的,除非要求用户去清除本地缓存

CSP

CSP(Content-Security-Policy)

  • 作用
    • 限制资源获取
    • 报告资源获取越权
  • 限制方式
    • default-src限制全局
    • 指定资源类型
  • 资源类型(基本上所有外链)
    • connect-src(ajax请求)
    • img-src
    • manifest-src
    • style-src
    • font-src
    • media-src
    • frame-src
    • script-src
    • ...
  • 基本示例
    // 设定只能通过http和https加载'Content-Security-Policy':'default-src http: https:'
    // 指定本域名进行加载
    'Content-Security-Policy':'default-src \'self\''
    // 指定某个网站
    'Content-Security-Policy':'default-src \'self\' https://cdn.bootcss.com'
    // 指定表单
    'Content-Security-Policy':'default-src \'self\';form-action \'self\'
    // 回报
    'Content-Security-Policy':'default-src \'self\';report-uri /report
    // 允许加载,但是要上报
    'Content-Security-Policy-Report-Only':'default-src \'self\';report-uri /report
    
  • 不仅可以通过header来写,也可以通过meta标签使用,区别就是meta不能写report-uri
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
    

nginx

  • 单纯的HTTP WEB服务器
  • 现在互联网主要作用
    • 缓存
    • 负载均衡
  • mac直接使用brew安装即可
    • 配置文件目录:/usr/local/etc/nginx/
    • 实际安装目录:/usr/local/Cellar/nginx
    • 直接输入nginx即可启动服务,默认8080端口(brew安装)
    • 停止服务:nginx -s stop
    • 重新加载配置:nginx -s reload
  • include参数,用以导入参数,可以帮助我们隔离不同的服务
  • proxy_pass设置代理服务器
  • proxy_set_header headerKey headerValue
    # nginx变量 $host,可以保证实际服务器得到的host为浏览器传输的host,而不是nginx代理服务器地址
    proxy_set_header Host $host;
    
  • 在代理过程中,由于HTTP明文传输,所有东西都是可以被篡改的

代理缓存功能

  • proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m
    • cache表示目录
    • levels是否创建二级文件夹
    • keys_zone缓存内存大小
  • proxy_cache my_cache
  • 浏览器本身也会缓存,那么代理缓存的意义
    • 浏览器缓存只能针对当前浏览器
    • 开启代理缓存后,缓存可以被所有客户端使用
  • 与代理缓存头相关请求头,具体解释见上
    • Cache-Control:s-maxage\private\no-store
    • Vary,指定一个headerKey,下次请求必须确保headerKey对应的headerValue相同才会启用缓存,使用场景:缓存开启后,会根据User-Agent不一样,比如针对PC端和移动端数据不一样,则可以启用此功能。这个头很重要哟,因为不是只根据URL就够了。
  • 功能提升:缓存使用内存数据库,因此默认是写磁盘的

HTTPS解析

  • 加密,私钥+公钥

WX20180626-080436@2x.png

  • 部署HTTPS服务
  • 生成公钥和私钥(openssl工具)
    openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-privkey.pem -out localhost-cert.pem
    

HTTP/2.0

  • 优势
    • 信道复用
    • 分帧传输
    • Server Push
  • 信道复用
    • 在HTTP/1.1中如果需要加载很多资源,并发发送受限于浏览器最大的TCP连接数,因为在一个TCP连接上,比如客户端必须等第一个请求发送完成后才能发送第二个请求,同时服务端必须等前一个请求数据全部返回后才能返回第二个请求数据,实质还是串行
    • 在HTTP/1.1中,创建6个(Chrome)TCP连接开销很大,同时服务器的最大TCP连接数也是有限制的
    • 在HTTP/2.0后,一个用户永远只需要一个TCP连接,因为可以并发发送和返回
  • HTTP/2.0需要HTTPS支持,使用nginx开启HTTP2有个好处,就是会兼容HTTP1.1


留言