http深入浅出

874 阅读15分钟
原文链接: yanlee26.github.io

TODO: 持续更新中。。。

1. 简介

HTTP 协议是互联网的基础协议,也是网页开发(无论前后端)的必备知识。若想在专业技术道路上走得更坚实,绝对不可能绕开学习 HTTP 协议这一环节。对基础及核心部分的深入学习是成为一名优秀技术人员的前提。

2. http/https 背景及发展简介,基本概念简介

HTTP 是基于 TCP/IP 协议的应用层协议。它不涉及数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用 80 端口。

  • 网络基础 TCP/IP:
  • HTTP 超文本传输协议(Hypertext Transfer Protocol):

用于传输超媒体文档(如 HTML)的应用层协议。 它专为 Web 浏览器和 Web 服务器之间的通信而设计,但也可用于其他目的。 HTTP 遵循传统的客户端-服务器模型,客户端打开连接以发出请求,然后等待它收到响应。 HTTP 是无状态协议,这意味着服务器不会在两个请求之间保留任何数据(状态)。 虽然通常基于 TCP / IP 层,但它可以在任何可靠的传输层上使用; 也就是说,一种不会无声地丢失消息的协议,例如 UDP。

  • HTTP 是一种允许获取资源的协议,例如 HTML 文档。 它是 Web 上任何数据交换和客户端- 服务器协议的基础,这意味着请求由接收者(通常是 Web 浏览器)发起。 从所获取的不同子文档重建完整文档,例如文本,布局描述,图像,视频,脚本等。

  • URI/URL/URN:
    • URI(Uniform Resource Indentifier):由某个协议方案表示等资源定位标识符。包含 UIL(Location)和 URN(永久资源定位符)
  • 报文格式:

三次握手

确保数据到达目标:三次 🤝(three-way handshaking) TCP 的 flag:SYN(synchronize)和 ACK(acknowledgement)

  • syn
  • syn + ack
  • ack

http 发展

  • http/0.9
    • 只有一个命令 get
    • 协议规定,服务器只能回应 HTML 格式的字符串,不能回应别的格式。
    • 没有 header 等描述数据的信息
    • 服务器发送完毕,tcp 连接就关闭
  • http/1.0

    • 任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件
    • 更多命令(GET 命令,还引入了 POST 命令和 HEAD)
    • HTTP 请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
    • 其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。
    • HTTP/1.0 版的主要缺点是,每个 TCP 连接只能发送一个请求。
    • Content-Type 字段:关于字符的编码,1.0 版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式
  • http/1.1
    • 持久连接(persistent connection):TCP 连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive
    • pipeline:在同一个 TCP 连接里面,客户端可以同时发送多个请
    • Content-Length 字段:一个 TCP 连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应,声明本次回应的数据长度.
    • 分块传输编码:产生一块数据,就发送一块,采用”流模式”(stream)取代”缓存模式”(buffer)
    • 增加 host 和其它命令(PUT、PATCH、HEAD、 OPTIONS、DELETE),客户端请求的头信息新增了 Host 字段,用来指定服务器的域名.Host: mp.weixin.qq.com
    • 缺点:队头堵塞,为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入 CSS 代码、域名分片(domain sharding)等等。
  • SPDY 协议:2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础

  • http2(2015 年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。)
    • 只有在 HTTPS 环境才会生效
    • 二进制协议:头信息和数据体都是二进制,并且统称为”帧”(frame):头信息帧和数据帧。可以定义额外的帧
    • 多工(Multiplexing):同一个连接里发送多个请求无需按顺序
    • 数据流:每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流 ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID 一律为奇数,服务器发出的,ID 为偶数。HTTP/2 可以取消某一次请求,同时保证 TCP 连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。
    • 头信息压缩(header compression): 推送更高效,一方面,头信息使用 gzip 或 compress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
    • 服务器推送(server push):HTTP/2 允许服务器未经请求,主动向客户端发送资源。服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

3. http 常见特性总结及梳理

4. 常见跨域情形,几种常见跨域解决方案及原理

浏览器同源政策及其规避方法

  • 同源策略(Same-origin_policy)
  • 同源:协议&&域名&&端口三者均相同;
  • 同源目的:保证用户信息的安全,防止恶意的网站窃取数据;
  • 同源限制:
    • Cookie、LocalStorage 和 IndexDB 无法读取;
    • DOM 无法获得;
    • AJAX 请求不能发送
  • Ajax:同源政策规定,AJAX 请求只能发给同源的网址.除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制:
    • jsonp:简单,老式浏览器全部支持,服务器改造非常小.不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。但只支持 GET 请求。
    • websocket:WebSocket 是一种通信协议,使用 ws://(非加密)和 wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。有一个字段是 Origin,表示该请求的请求源(origin),即发自哪个域名.
    • CORS:
      • CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
      • 请求分类:
        • 简单请求(simple request):
          1. 请求方法是以下三种方法之一:HEAD/GET/POST
          2. HTTP 的头信息不超出以下几种字段: Accept/Accept-Language/Content-Language/Last-Event-ID/Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
        • 非简单请求(not-so-simple request):非上
      • 请求分类处理:
        • 简单请求:
          • 头信息之中,增加一个 Origin 字段:说明,本次请求来自哪个源(协议 + 域名 +端口)。服务器根据这个值,决定是否同意这次请求。
          • 如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
            • Access-Control-Allow-Origin:必须,接受请求的域名
            • Access-Control-Allow-Credentials:可选,是否允许发送 Cookie。如果要把 Cookie 发到服务器,一方面要服务器同意,指定 Access-Control-Allow-Credentials 字段。Access-Control-Allow-Credentials: true;另一方面,开发者必须在 AJAX 请求中打开 withCredentials 属性。var xhr = new XMLHttpRequest();xhr.withCredentials = true; ⚠️:如果要发送 Cookie,Access-Control-Allow-Origin 就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的 Cookie

            • Access-Control-Expose-Headers:可选。CORS 请求时,XMLHttpRequest 对象的 getResponseHeader()方法只能拿到 6 个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定
        • 非简单请求:对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json。非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为”预检”请求(preflight)。
          • 预检请求:非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为”预检”请求(preflight)。
            OPTIONS /cors HTTP/1.1
            Origin: http://api.bob.com
            Access-Control-Request-Method: PUT //CORS请求会用到哪些HTTP方法
            Access-Control-Request-Headers: X-Custom-Header
            Host: api.alice.com // 浏览器CORS请求会额外发送的头信息字段
            Accept-Language: en-US
            Connection: keep-alive
            User-Agent: Mozilla/5.0...
            
          • 预检响应:
            HTTP/1.1 200 OK
            Date: Mon, 01 Dec 2008 01:15:39 GMT
            Server: Apache/2.0.61 (Unix)
            Access-Control-Allow-Origin: http://api.bob.com  //同意跨源的url
            Access-Control-Allow-Methods: GET, POST, PUT //服务器支持的所有跨域请求的方法
            Access-Control-Allow-Headers: X-Custom-Header //服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段  
            Access-Control-Max-Age :3600 //本次预检请求的有效期,单位为秒。
            Content-Type: text/html; charset=utf-8
            Content-Encoding: gzip
            Content-Length: 0
            Keep-Alive: timeout=2, max=100
            Connection: Keep-Alive
            Content-Type: text/plain
            

跨域通用解决方案

5. RESTful 简介

REST(REpresentational State Transfer)这个概念,首次出现是在 2000 年 Roy Thomas Fielding(他是 HTTP 规范的主要编写者之一)的博士论文中,它指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful 的。

简单地理解就是@Ivony 老师URL 定位资源,用 HTTP 动词(GET,POST,DELETE,DETC)描述操作。

要理解什么是 REST,我们需要理解下面几个概念:

  • 资源(Resources) REST 是”表现层状态转化”,其实它省略了主语。”表现层”其实指的是”资源”的”表现层”。

    那么什么是资源呢?就是我们平常上网访问的一张图片、一个文档、一个视频等。这些资源我们通过 URI 来定位,也就是一个 URI 表示一个资源。

  • 表现层(Representation)

    资源是做一个具体的实体信息,他可以有多种的展现方式。而把实体展现出来就是表现层,例如一个 txt 文本信息,他可以输出成 html、json、xml 等格式,一个图片他可以 jpg、png 等方式展现,这个就是表现层的意思。

    URI 确定一个资源,但是如何确定它的具体表现形式呢?应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对”表现层”的描述。

  • 状态转化(State Transfer)

    访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,肯定涉及到数据和状态的变化。而 HTTP 协议是无状态的,那么这些状态肯定保存在服务器端,所以如果客户端想要通知服务器端改变数据和状态的变化,肯定要通过某种方式来通知它。

    客户端能通知服务器端的手段,只能是 HTTP 协议。具体来说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源。

综合上面的解释,我们总结一下什么是 RESTful 架构:

  • (1)每一个 URI 代表一种资源;
  • (2)客户端和服务器之间,传递这种资源的某种表现层;
  • (3)客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现”表现层状态转化”。

Web 应用要满足 REST 最重要的原则是:客户端和服务器之间的交互在请求之间是无状态的,即从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外此请求可以由任何可用服务器回答,这十分适合云计算之类的环境。因为是无状态的,所以客户端可以缓存数据以改进性能。

另一个重要的 REST 原则是系统分层,这表示组件无法了解除了与它直接交互的层次以外的组件。通过将系统知识限制在单个层,可以限制整个系统的复杂性,从而促进了底层的独立性。

下图即是 REST 的架构图:

图 8.5 REST 架构图

当 REST 架构的约束条件作为一个整体应用时,将生成一个可以扩展到大量客户端的应用程序。它还降低了客户端和服务器之间的交互延迟。统一界面简化了整个系统架构,改进了子系统之间交互的可见性。REST 简化了客户端和服务器的实现,而且对于使用 REST 开发的应用程序更加容易扩展。

下图展示了 REST 的扩展性:

图 8.6 REST 的扩展性

RESTful 的实现

Go 没有为 REST 提供直接支持,但是因为 RESTful 是基于 HTTP 协议实现的,所以我们可以利用net/http包来自己实现,当然需要针对 REST 做一些改造,REST 是根据不同的 method 来处理相应的资源,目前已经存在的很多自称是 REST 的应用,其实并没有真正的实现 REST,我暂且把这些应用根据实现的 method 分成几个级别,请看下图:

图 8.7 REST 的 level 分级

上图展示了我们目前实现 REST 的三个 level,我们在应用开发的时候也不一定全部按照 RESTful 的规则全部实现他的方式,因为有些时候完全按照 RESTful 的方式未必是可行的,RESTful 服务充分利用每一个 HTTP 方法,包括DELETEPUT。可有时,HTTP 客户端只能发出GETPOST请求:

  • HTML 标准只能通过链接和表单支持GETPOST。在没有 Ajax 支持的网页浏览器中不能发出PUTDELETE命令

  • 有些防火墙会挡住 HTTP PUTDELETE请求,要绕过这个限制,客户端需要把实际的PUTDELETE请求通过 POST 请求穿透过来。RESTful 服务则要负责在收到的 POST 请求中找到原始的 HTTP 方法并还原。

我们现在可以通过POST里面增加隐藏字段_method这种方式可以来模拟PUTDELETE等方式,但是服务器端需要做转换。我现在的项目里面就按照这种方式来做的 REST 接口。当然 Go 语言里面完全按照 RESTful 来实现是很容易的,我们通过下面的例子来说明如何实现 RESTful 的应用设计。

6. Nginx 特点,概念及常见配置

TODO:

7. https(TLS)简介及升级

  • 图解 SSL/TLS 协议
  • SSL/TLS 协议运行机制的概述
  • TLS 协议:Transport Layer Security protocol
  • 握手过程:开始加密通信之前,客户端和服务器首先必须建立连接和交换参数,这个过程叫做握手(handshake)。
    • 第一步,爱丽丝给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。

    • 第二步,鲍勃确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。

    • 第三步,爱丽丝确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。

    • 第四步,鲍勃使用自己的私钥,获取爱丽丝发来的随机数(即 Premaster secret)。

    • 第五步,爱丽丝和鲍勃根据约定的加密方法,使用前面的三个随机数,生成”对话密钥”(session key),用来加密接下来的整个对话过程。

如何将一个 HTTP 网站升级到 HTTPS

  • 一、获取证书:证书是一个二进制文件,里面包含经过认证的网站公钥和一些元数据,要从经销商购买。
  • 二、安装证书
  • 三、修改链接:网页加载的 HTTP 资源,要全部改成 HTTPS 链接
<!-- 改法一 -->
<script src="https://foo.com/jquery.js"></script>
<!-- 改法二:根据当前网页的协议,加载相同协议的外部资源,更灵活一些 -->
<script src="//foo.com/jquery.js"></script>
  • 四、301 重定向:修改 Web 服务器的配置文件,使用 301 重定向,将 HTTP 协议的访问导向 HTTPS 协议
  • 五、安全措施
    • 确保浏览器只在使用 HTTPS 时,才发送 Cookie。 网站响应头里面,Set-Cookie 字段加上 Secure 标志即可。
Set-Cookie: LSID=DQAAAK...Eaem_vYg; Secure

8. 参考连接