HTTP 相关知识了解一下

首先了解下浏览器输入url后http请求返回的过程是什么,看下图javascript

(一) 首先一开始要作 redirect 重定向,那么为何要 redirect 呢,由于浏览器可能记录了你这个地址已经永久跳转成一个新的地址,因此一开始浏览器须要判断需不须要 redirect 以及 redirect 到哪里。css

(二) 看缓存,请求的资源可能已经缓存过,在 App cache 里看是否有缓存,若是没有缓存,就会去服务器请求资源。html

(三) 输入域名,域名会对应ip以后才能真正访问到服务器,因此这时候会先去查找域名对应的IP地址,这就叫DNS解析前端

(四) 有了IP以后,就会建立tcp链接,该过程要通过tcp的三次握手以后才能真正建立链接。同时若是这个请求是https的,就会建立https的连接,这跟tcp的三次握手不同,中间会有一个保证安全的数据传输的过程。java

(五) 链接建立好以后,才会真正发起http请求的数据包,数据包发送完成以后,服务器接收到这个数据并进行处理以后会返回这个请求响应的内容数据,返回数据以后这个http请求才真正完成node

1. 网络协议分层

咱们先来看下经典五层模型图例:nginx

这个五层模型中,分为应用层、传输层、网络层、数据链路层、物理层,每个服务器上都会有这样的层级关系存在来维护整个网络数据传输的过程。web

本文主要内容是http相关内容,因此主要是在应用层展开。http协议基于传输层里的 TCP/IP 协议,该协议会涉及到一些http请求的性能,以及请求过程的消耗,会面也会有一些 TCP/IP 协议的介绍。ajax

低三层:算法

  • 物理层(硬件) - 主要做用是定义物理设备如何传输数据
  • 数据链路层 - 在通讯的实体间创建数据链路链接
  • 网络层 - 为数据在结点之间传输建立逻辑链路

1.1 传输层

传输层主要有两个协议,一个是 TCP/IP,一个是 UDP。更多状况下都使用的是 TCP/IP 协议,由于它更可靠的传输数据。

向用户提供可靠的端到端(e2e)服务

我的电脑到网络服务器创建链接以后,若是传输数据很大,一次性没法完成传输,就须要分片传输,传输成功以后再从新组装。两端传输数据的方式都是在这一层定义

传输层向高层屏蔽了下层数据通讯的细节

http协议要传输数据只需在浏览器输入url,输入url这个过程还涉及到一系列数据的拼装及传输,好比分包传输具体是怎么实现,服务器怎么接收,ajax请求,整个过程传输层已经作好了封装,这个过程用户不须要知道。

1.2 应用层

http协议所在层

  • 为应用软件提供了不少服务
  • 构建于TCP协议之上
  • 屏蔽网络传输相关细节

2. http协议发展历史

  • HTTP/0.9
    • 只有一个命令GET
    • 没有HEADER等描述数据的信息
    • 服务器发送完毕,就关闭TCP链接
  • HTTP/1.0
    • 增长了不少命令
    • 增长 status code 和 header
    • 多字符集支持、多部分发送、权限、缓存等
  • HTTP/1.1
    • 持久链接
    • pipeline
    • 增长 host 和其它一些命令
  • HTTP2
    • 全部数据以二进制传输
    • 同一个链接里面发送多个请求再也不须要按照顺序来
    • 头信息压缩以及推送等提升效率的功能

头信息压缩及推送

http2 解决了 http 里总体性能低下的问题,在http1.1里,每次发送请求和返回请求,它的http头都会进行一个完整传输,而且不少字段都是以字符串形式保存,占用大量带宽。http2里会将头信息进行压缩传输。

推送是什么概念呢?在http1.1里,客户端发起请求而后服务端响应请求返回内容,客户端永远是主动方,服务端永远是被动方。在http2里,服务端是能够主动发起数据传输的。好比:一个html页面中引入了css和js,浏览器首先要对html进行分析,再寻找css和js对应的url去请求对应的文件,这就涉及到一个顺序问题,须要先请求到html文本,在浏览器里运行解析了这个文本以后才能发送css及js的请求。可是在http2中服务端能够主动把css及js文件推送到客户端,与html并行传输,极大提升传输效率

3. http的链接

在客户端和服务端之间进行http请求的发送和返回的过程中,须要建立一个 TCP connection。http只有请求和响应这个概念,不存在链接,请求和响应都是数据包, 之间要通过一个传输的通道,这个传输的通道就是在tcp里建立的一个链接(TCP connection)。这个链接能够保持状态,http请求就是在这个链接之上发送的,因此在一个tcp链接上能够发送多个http请求。

在不一样版本下,http链接的模式不同,在http1.0里,这个链接是在http请求建立同时建立tcp链接,请求结束后tcp链接就会关闭。

在http1.1里,这个链接能够经过某种方式声明是否保持链接状态。tcp链接在建立过程当中有三次握手消耗,三次握手就表示三次网络传输(客户端发送 - 服务端响应 - 客户端再次发送),而后才能发送http请求。若是tcp链接一直保持,第二个http请求就没有三次握手的开销

在http2里不只能够保持tcp的链接,同时这个链接上的http请求能够并发,就是说同一个用户对同一个服务器发起一个网页请求时只须要一个tcp链接。

3.1 tcp的三次握手

(一) 在tcp的三次握手当中,客户端会向服务端发起一个建立链接的数据包请求,这里会有一个标识为 SYN=1,SYN是一个标志位,表示建立请求的数据包。后面会发送一个叫 Seq=X,X表示数字,通常为1。服务端接收到这个数据包以后就会知道要建立一个链接

(二) 建立链接以后,服务端就会开启一个 tcp 端口,返回给客户端数据,这个数据 SYN=1,ACK=X+1,Seq=Y,客户端拿到这个数据表示服务端容许建立这个tcp链接。

(三) 这时候客户端再去发送 ACK=Y+1,Seq=Z

那么为何tcp要进行三次握手呢,这是为了防止服务端开启一些无用链接,网络链接具备延时性。客户端向服务端发起建立链接请求时,服务端直接建立了这个链接,返回的数据包由于网络缘由丢失,那么客户端就一直接收不到服务器返回的数据,链接超时这个链接就会关闭,而后再发起新的链接,若是没有三次握手的话,这时服务端是不知道客户端到底有没有接收到返回的数据,浪费服务端的开销。因此须要三次握手去即时的察觉到网络缘由致使的数据包传输中断的问题。

3.2 URI、URL、URN

在http协议中,基本上使用的都是URL。

  • URI(Uniform Resource Identifier)
    • 统一资源标志符
    • 用来惟一标识互联网上的信息资源
    • 包含URL和URN
  • URL(Uniform Resource Locator)
    • 统一资源定位器
  • URN
    • 永久统一资源定位符
    • 在资源移动以后还能被找到
    • 目前尚未很是成熟的使用方案

3.3 HTTP报文

从图中能够看到http首部下面有一段空行,空行下面表示 http body 部分,上面就是 http headers 部分。

3.3.1 请求报文

(一) GET

http请求头中,首行第一部分包含的是 method 请求方法,每一个方法有各自的语义,分别是 GET(获取数据)、POST(建立数据)、PUT(更新数据)、DELETE(删除数据)。这几种方法只是语义上的说明,并无强约束,好比使用 GET 方法去更新数据,只是这样违反了http语义化的定义规则。

(二) /test/hi-there.txt

首行第二部分是请求的资源资源地址url,通常这里是存放路由相关的内容

(三) HTTP/1.0

首行第三部分是http的版本,如今的web服务通常都是http1.1,http2也会有愈来愈多的实现,不一样的版本也会有不一样操做方式。

3.3.2 响应报文

(一) 200 ok

http状态码,200 表明成功

3.4 HTTP方法

  • 用来定义对于资源的操做
  • 经常使用有GET、POST等
  • 从定义上有各自的语义
GET - 请求指定的页面信息,并返回实体主体。
HEAD - 相似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
POST - 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会致使新的资源的创建和/或已有资源的修改。
PUT - 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE - 请求服务器删除指定的页面。
复制代码

3.5 HTTP CODE

  • 定义服务器对请求的处理结果
  • 各个区间的CODE有各自的语义
  • 好的HTTP服务能够经过CODE判断结果

HTTP Status:

1xx(临时响应)
表示临时响应并须要请求者继续执行操做的状态代码。

代码   说明
100   (继续) 请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其他部分。
101   (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。

2xx (成功)
表示成功处理了请求的状态代码。

代码   说明
200   (成功)  服务器已成功处理了请求。 一般,这表示服务器提供了请求的网页。
201   (已建立)  请求成功而且服务器建立了新的资源。
202   (已接受)  服务器已接受请求,但还没有处理。
203   (非受权信息)  服务器已成功处理了请求,但返回的信息可能来自另外一来源。
204   (无内容)  服务器成功处理了请求,但没有返回任何内容。
205   (重置内容) 服务器成功处理了请求,但没有返回任何内容。
206   (部份内容)  服务器成功处理了部分 GET 请求。

3xx (重定向)
表示要完成请求,须要进一步操做。 一般,这些状态代码用来重定向。

代码   说明
300   (多种选择)  针对请求,服务器可执行多种操做。 服务器可根据请求者 (user agent) 选择一项操做,或提供操做列表供请求者选择。
301   (永久移动)  请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302   (临时移动)  服务器目前从不一样位置的网页响应请求,但请求者应继续使用原有位置来进行之后的请求。
303   (查看其余位置) 请求者应当对不一样的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304   (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
305   (使用代理) 请求者只能使用代理访问请求的网页。 若是服务器返回此响应,还表示请求者应使用代理。
307   (临时重定向)  服务器目前从不一样位置的网页响应请求,但请求者应继续使用原有位置来进行之后的请求。

4xx(请求错误)
这些状态代码表示请求可能出错,妨碍了服务器的处理。

代码   说明
400   (错误请求) 服务器不理解请求的语法。
401   (未受权) 请求要求身份验证。 对于须要登陆的网页,服务器可能返回此响应。
403   (禁止) 服务器拒绝请求。
404   (未找到) 服务器找不到请求的网页。
405   (方法禁用) 禁用请求中指定的方法。
406   (不接受) 没法使用请求的内容特性响应请求的网页。
407   (须要代理受权) 此状态代码与 401(未受权)相似,但指定请求者应当受权使用代理。
408   (请求超时)  服务器等候请求时发生超时。
409   (冲突)  服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
410   (已删除)  若是请求的资源已永久删除,服务器就会返回此响应。
411   (须要有效长度) 服务器不接受不含有效内容长度标头字段的请求。
412   (未知足前提条件) 服务器未知足请求者在请求中设置的其中一个前提条件。
413   (请求实体过大) 服务器没法处理请求,由于请求实体过大,超出服务器的处理能力。
414   (请求的 URI 过长) 请求的 URI(一般为网址)过长,服务器没法处理。
415   (不支持的媒体类型) 请求的格式不受请求页面的支持。
416   (请求范围不符合要求) 若是页面没法提供请求的范围,则服务器会返回此状态代码。
417   (未知足指望值) 服务器未知足”指望”请求标头字段的要求。

5xx(服务器错误)
这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误多是服务器自己的错误,而不是请求出错。

代码   说明
500   (服务器内部错误)  服务器遇到错误,没法完成请求。
501   (还没有实施) 服务器不具有完成请求的功能。 例如,服务器没法识别请求方法时可能会返回此代码。
502   (错误网关) 服务器做为网关或代理,从上游服务器收到无效响应。
503   (服务不可用) 服务器目前没法使用(因为超载或停机维护)。 一般,这只是暂时状态。
504   (网关超时)  服务器做为网关或代理,可是没有及时从上游服务器收到请求。
505   (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。
复制代码

4. HTTP客户端

浏览器就是咱们最经常使用的http客户端

同时 curl 能够查看http请求返回的内容

curl还能够查看请求的详细内容, curl -v [host]

4.1 CORS跨域请求的限制与解决

作过前端开发的同窗对跨域并不会陌生,一般咱们可使用jsonp去实现跨域请求。

jsonp跨域:

<script src="http://www.example.com:8080"></script>
复制代码

原理:

浏览器容许 link、img、script 标签上写入路径加载一些内容的时候,是容许跨域的。

服务端设置跨域:

这里以express为例,咱们只需在响应头中添加 Access-Control-Allow-Origin 便可

response.writeHead(200, {
  'Access-Control-Allow-Origin': '*'
})
复制代码

浏览器在发送请求的时候并不知道服务是否跨域,仍是会发送请求而且接收返回内容,只是在浏览器接收内容的时候没有找到 Access-Control-Allow-Origin 头设置为容许的话,它会把请求返回的内容忽略掉而且会在服务端报错。这个是浏览器所提供的功能。

实际上 Access-Control-Allow-Origin 值为 * 是不安全的,这样会致使第三方服务也能够经过跨域访问你的服务,能够设置为特定的域名

response.writeHead(200, {
  'Access-Control-Allow-Origin': 'http://xxxx.com'
})
复制代码

4.1.1 CORS预请求

浏览器是根据 header 判断某个请求的返回是否容许,若是想要容许自定义的头进行发送的话,须要返回新的头告知浏览器容许

response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'X-Test-Cors'
})
复制代码

同时会发现 network 中多出一个请求,这就是预请求,它的 Request MethodOPTIONS,服务端能够根据不一样method进行不一样的操做,浏览器根据这个 OPTIONS 请求,来得到服务端容许的权限,接下来发送的它所承认的请求就会被容许,这就是浏览器对于跨域请求的预请求操做。

(一) 在跨域请求中,默认容许的方法只有 GET、HEAD、POST,其它的方法默认不容许,浏览器是有一个预请求的方式去验证的。

response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'PUT'
})
复制代码

还能够设置某个请求下容许跨域的最大时间,这样就不须要再次发送预请求去验证,可直接发送正式请求

response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'PUT',
  'Access-Control-Max-Age': '1000'
})
复制代码

(二) 默认容许的Content-Type:text/plain、multipart/form-data、application/x-www-form-urlencoded。这三个就是在html里使用form表单能够设置的三种数据类型。其它的也须要预请求验证过以后才能进行发送。

(三) 其它的请求头限制具体能够查看文档 https://fetch.spec.whatwg.org/#cors-safelisted-request-header

(四) XMLHttpRequestUpload 对象均没有注册任何事件监听器

(五) 请求中没有使用 ReadableStream 对象

4.2 缓存Cache-Control

  • 可缓存性
    • public(任何地方都会缓存)
    • private(发起请求的浏览器)
    • no-cache 能够在本地进行缓存,可是每次发起请求都要在服务端验证,若是服务端容许使用本地缓存,才能真正使用本地缓存。
  • 到期
    • max-age = <seconds>
    • s-maxage = <seconds> 在代理服务器中才会生效,代理服务器中若是两个都设置了,会优先选择 s-maxage 配置项
    • max-stale = <secon ds> 发起端设置。即使缓存失效,只要这个时间内还可使用过时的缓存,而不须要去原服务器请求新的内容。
  • 从新验证(不经常使用)
    • must-revalidate 在设置了max-age缓存中若是过时,必须去原服务端发送请求而后从新获取数据再来验证内容是否真的过时,而不能直接使用本地缓存。
    • proxy-revalidate 和 must-revalidate 相似,可是用在缓存服务器当中。
  • 其它
    • no-store 与 no-cache 对应,表示任何状况下都不会存储缓存,永远都要去服务端请求新的内容才能使用它。即使服务端容许使用缓存,但本地没有进行缓存
    • no-transform 用在 proxy 服务器,有些proxy服务器返回资源过大,会帮助进行压缩及格式转换,该属性会不容许。

没有缓存状况下请求资源

设置客户端缓存后

response.writeHead(200, {
  'Content-Type': 'text/javascript',
  'Cache-Control': 'max-age=200'
})
response.end('console.log("script loaded")')
复制代码

能够看到 Size 变为了 == (from memory cache) ==,表示从浏览器中读取缓存。

在作前端开发的时候,有些静态资源文件咱们但愿浏览器缓存下来,可是在服务端内容更新以后,客户端请求的是缓存下的旧资源文件,这样就无法更新应用。

目前最多见的方式就是前端编译的时候加静态资源文件md5戳。

4.3 资源验证

首先看一张图

浏览器建立请求,请求到达本地缓存(若是有cache-control),若是有本地缓存,就直接返回给浏览器显示出来,不通过任何网络传输。若是没有本地缓存,请求进入网络传输,若是有代理服务器就会进入并查找缓存设置,查看资源是否被缓存,有缓存就返回缓存资源通过本地缓存再到浏览器显示。若是代理服务器未缓存,就会进入原服务器获取新的内容再返回。

4.3.1 验证头

  • Last-Modified
    • 上次修改时间
    • 配合If-Modified-Since或者If-Unmodified-Since使用

浏览器在请求资源的headers里有 Last-Modified 这个头,并指定了时间,这个时间内下次浏览器发起请求时就会带上 Last-Modified传入的值,经过 If-Modified-SinceIf-Unmodified-Since(一般为If-Modified-Since)带到服务器上,服务器经过读取 headers里If-Modified-Since 带入的值找到资源存在的地方对比上次修改的时间,若是时间同样,就表示资源没有被从新修改过,服务器就通知浏览器直接使用缓存的资源,这就是资源验证的过程。

  • Etag(更加严格的验证方式)
    • 数据签名 - 对内容产生惟一的签名
    • 配合If-Match或者If-Non-Match使用

有任何的修改两个签名就会不同,最典型的作法就是对资源内容作哈希计算,计算以后会获得一个惟一值,用这个签名来标记这个资源,下一次浏览器发起请求时会带上 If-Match或者If-Non-Match头,这个头的值就是服务端返回Etag的值,而后对比服务器拿到浏览器传入的签名和服务器存在的签名,若是相同,就不须要返回新的内容。

4.4 Cookie和Session

  • Cookie

    • 经过Set-Cookie设置
    • 下次请求会自动带上
    • 键值对,能够设置多个
  • Cookie属性

    • max-age(有效时间)和expires(到某个时间点过时)设置过时时间
    • Secure只在https的时候发送
    • HttpOnly没法经过document.cookie访问
// Cookie设置,以express为例
response.writeHead(200, {
  'Content-Type': 'text/html',
  'Set-Cookie': 'name=hello'
})

// 设置多个
'Set-Cookie': ['name=hello', 'age=12']

// 过时时间
'Set-Cookie': ['name=hello; max-age=2', 'age=12']

// 禁止js访问cookie
'Set-Cookie': ['name=hello', 'age=12; HttpOnly']

// 子域名共享主域名cookie,前提是cookie要在主域名下设置
'Set-Cookie': ['name=hello', 'age=12; domain=example.com']
复制代码

Session

session机制是一种服务器端的机制,服务器使用一种相似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序须要为某个客户端的请求建立一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,若是已包含一个session id则说明之前已经为此客户端建立过session,服务器就按照session id把这个session检索出来使用(若是检索不到,可能会新建一个),若是客户端请求不包含session id,则为此客户端建立一个session而且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

保存这个session id的方式能够采用cookie,这样在交互过程当中浏览器能够自动的按照规则把这个标识发挥给服务器。通常这个cookie的名字都是相似于SEEESIONID,而。好比weblogic对于web应用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。

因为cookie能够被人为的禁止,必须有其余机制以便在cookie被禁止时仍然可以把session id传递回服务器。常常被使用的一种技术叫作URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是做为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764

另外一种是做为查询字符串附加在URL后面,表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764

这两种方式对于用户来讲是没有区别的,只是服务器在解析的时候处理的方式不一样,采用第一种方式也有利于把session id的信息和正常程序参数区分开来。为了在整个交互过程当中始终保持状态,就必须在每一个客户端可能请求的路径后面都包含这个session id。

另外一种技术叫作表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时可以把session id传递回服务器。好比下面的表单:

<form name="testform" action="/xxx">
  <input type="text">
</form>
复制代码

在被传递给客户端以前将被改写成:

<form name="testform" action="/xxx">
  <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
  <input type="text">
</form>
复制代码

这种技术如今已较少应用,实际上这种技术能够简单的用对action应用URL重写来代替。

4.5 HTTP长链接

http的请求是在tcp的链接上进行发送的,tcp链接又分为长链接和短链接。

长链接就是在tcp链接上把http请求发送并接收返回,这个时候一次http请求已经结束,浏览器和服务器会协商是否断掉这个链接,长链接就是在不断掉链接下能够持续发送http请求,适合高并发。

在Connection Id这一栏能够看到不少 10247 的相同id,表示这些请求都是在同一链接下发送的。但仍是会有不一样的链接,由于http1.1的链接在tcp链接上发送请求是有前后顺序的,不会并发请求。

咱们但愿在加载网站首页的时候能够并发处理这些请求,浏览器能够容许产生一个并发的建立tcp链接,chrome容许最大并发数为6。

能够看到,并发状况下会建立不一样的tcp链接,chrome若是超出了6个并发,后面的请求会等待前面的完成,而且会尽可能复用前面的链接地址而保持长链接,这是浏览器默认的行为。

能够手动关闭长链接

response.writeHead(200, {
  'Content-Type': 'image/jpg',
  'Connection': 'close'
})
复制代码

关闭长链接以后能够发现每次链接id都会不同,后面的也会等待前面的完成,没有重复利用tcp链接,每次请求发送完成tcp链接就会关闭。

通常状况下keep-alive都是开启的,而且会设置一个自动关闭时间。

信道复用

在tcp链接上并发的发送http请求,也就是说在链接一个网站时只需一个tcp链接,只在同域下请求有效,http2实现了这个功能。

4.6 数据协商

在客户端发送给服务端请求的时候,客户端会声明这个请求拿到的数据格式以及数据相关的一些限制是怎样的,服务端会根据客户端的这个声明作出判断,从而返回不一样的数据类型格式。

分类

  • 请求
    • Accept
    • Accept-Encoding
    • Accept-Language
    • User-Agent
  • 返回
    • Content-Type
    • Content-Encoding
    • Content-Language

Accept里MIME_types相关对照表看这里文档

4.7 Redirect

在开发中,咱们存放资源的位置若是发生了改变,页面在请求时就会报404错误,为了不这种错误,须要帮助浏览器指向到正确的地址。

http.createServer(function(req, res) {
  if(req.url === '/'){
    res.writeHead(302, {
      'Location': '/new'
    })
    res.end('')
  }

  if(req.url === '/new'){
    res.writeHead(200, {
      'Content-Type': 'text/html'
    })
    res.end('<div>web</div>')
  }
})
复制代码

图中能够看到第一次请求页面状态码为302,并跳转到了new这个url下。

可是 302 是临时跳转,每一次访问都要通过服务端的跳转,图中也能够看到有两个url的请求。若是肯定每次访问 / 下都会跳转到 new 下,能够指定状态码为 301 永久跳转,这样访问页面就只出现 new。

须要注意的是,301 会尽量长时间的把跳转页面缓存下来,这时候服务端即便修改了url,浏览器仍是会从缓存里读取,这个是有用户使用浏览器状况所决定的,除非用户主动去清理浏览器缓存。因此 301 要慎重处理。

4.8 CSP

Content-Security-Policy内容安全策略,这使得浏览器变得更加安全。

做用

  • 限制资源获取
  • 报告资源获取越权

限制方式

  • default-src限制全局
  • 制定资源类型
    • connect-src
    • img-src
    • manifest-src
    • font-src
    • media-src
    • style-src
    • frame-src
    • script-src
res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy': 'default-src http: https:'
})
复制代码

加入限制以后浏览器就会阻止js脚本的加载并报错

只容许本站下的资源

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy': 'default-src \'self\''
})
复制代码

容许某些站点的内容

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy': 'default-src \'self\' http://www.example.com/'
})
复制代码

限制form表单的跳转

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy': 'default-src \'self\'; form-action \'self\''
})
复制代码

详细内容能够查看csp文档 MDN CSP

内容安全策略若是出现了不但愿出现的状况下,能够申请主动向服务端发起请求来汇报

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy': 'default-src \'self\' report-uri /report'
})
复制代码

若是咱们只想对限制进行错误报告而不阻止资源加载的话,能够这么写

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Content-Security-Policy-Report-Only': 'default-src \'self\''
})
```
scp不只能够写在 headers 里,还能够在html的meta标签里写
复制代码
``` 在meta下是不容许写 report-uri 的,这个指令只能写在 headers 里。

5. Nginx反向代理服务器

5.1 基础代理配置

nginx是如今互联网界用的最多的web服务器,它是一个很是纯粹的作http协议实现的服务器,并无一个工具来实现业务逻辑的开发。主要是用来作http的代理服务器。

nginx的安装和用法能够查网上相关教程。这里介绍的主要是nginx的代理和缓存的功能。

一个最简单的代理

// nginx.conf

server {
  listen       80;
  server_name  example.com;

  location / {
    proxy_pass http://127.0.0.1:8080;
    # proxy_set_header Host $host;
  }
}
复制代码

能够看到,浏览器下host 是 test.com,但在服务器下就变成了 127.0.0.1:8888。这是由于设置了代理,浏览器请求是发送到nginx的,nginx再进行转发,发送到实际的node服务,这时候做为发起方,它认为的 host 就是这里设置的 proxy_pass

想要拿到浏览器的 host 。能够设置 proxy_set_header 属性 $host

中间代理能够修改任何想要修改的数据,但只是在http中,https的传输过程是加密的,中间代理没法解析。在手机上所看到的一些移动联通的广告就是通过代理层插入了一些代码所展现的。

5.2 nginx缓存

// nginx.conf

proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;

server {
  listen       80;
  server_name  example.com;

  location / {
    proxy_cache my_cache;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
  }
}
复制代码

**proxy_cache_path:**第一个选项表示缓存路径,levels 是否建立二级文件夹,keys_zone url对应的缓存位置及内存大小

response.writeHead(200, {
  'Cache-Control': 'max-age=10, s-maxage=10, private'
})
复制代码

s-maxage 是专门为代理缓存设置过时时间的,而private就表示只容许浏览器缓存。

5.3 HTTPS

https在传输过程当中,客户端会生成一个随机数传输到服务端,中间会带上一个支持的加密套件,服务端拿到以后保存而且也生成一段随机数,而后把这段随机数和服务端生成的证书一同发到客户端,同时客户端也会把服务端的随机数保存,而且经过服务端证书生成预主秘钥,生成过程也会生成一个随机数,这个随机数经过公钥加密后传输给服务端,服务端经过私钥解密拿到预主秘钥。而后客户端和服务端同时对这三个随机数进行算法解密生成主密钥(这里会涉及到加密套件,服务端选择的加密套件必须是客户端所支持的),后续的数据传输都是通过主密钥加密进行传输的。这对主密钥只有客户端和服务端共有,中间代理没法破解,这就是https的加密原理。

这里是经过抓取工具抓的https加密的站点,能够看到,数据都被加密,没法破解。

5.3.1 nginx部署https服务

要部署https服务,首先要生成一对公钥和私钥,这里有一个命令能够帮助生成

openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-privkey.pem -out localhost-cert.pem
复制代码

敲入回车以后能够看到这样的提示,这里咱们测试,所有按回车跳过就好。

最终会生成两个文件,而后在nginx配置这个证书

// nginx.conf

proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;

server {
  listen       80 default_server;
  listen       [::]:80 default_server;
  server_name  test.com;
  return 302 https://$server_name$request_uri;
}

server {
  listen       443;
  server_name  test.com;

  ssl on;
  ssl_certificate_key /www/data/cert/localhost-privkey.pem;
  ssl_certificate /www/data/cert/localhost-cert.pem;

  location / {
    proxy_cache my_cache;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
  }
}
复制代码

配置好以后重启nginx服务,而后输入https的域名

提示非安全链接是由于chrome浏览器认为的安全证书是要经过有权威的机构去签发的,这种机构会先认证域名全部者与服务是否属于你,验证经过才会签发证书。

5.3.2 http2

  • 优点
    • 信道复用
    • 分帧传输
    • Server Push(推送)

http2的使用

res.writeHead(200, {
  'Content-Type': 'text/html',
  'Link': '</test.jpg>; as=image; rel=preload'
})
复制代码

server 头信息了的 Link能够指定这个头想要推送的内容,</xxx> 为文件绝对路径,as 指定文件类型,preload 表示须要进行服务端推送。

nginx里也要作这些配置。在使用nginx作反向代理时,咱们但愿nginx帮助处理这些东西,而 http2 也是在nginx里提供的,node server 仍是为http的服务,nginx 会把http2的请求转化为http的请求发送到node服务上。

为什么不在node上作http2的服务呢,由于在nginx开启一个http2的服务是很是容易的,在 node 上作http2的服务可能还会涉及到大量的逻辑修改,成本开销比较大。

须要注意的是,目前只有在https下才能开启http2

在nginx下开启http2很简单。

// nginx.conf

proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;

server {
  listen       80 default_server;
  listen       [::]:80 default_server;
  server_name  test.com;
  return 302 https://$server_name$request_uri;
}

server {
  listen       443 http2;
  server_name  test.com;
  http2_push_preload on;

  ssl on;
  ssl_certificate_key /www/data/cert/localhost-privkey.pem;
  ssl_certificate /www/data/cert/localhost-cert.pem;

  location / {
    proxy_cache my_cache;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
  }
}
复制代码

http2_push_preload 开启以后,在接收 node 返回信息里若是有 Link:rel=preload,就会去寻找该路径资源,而后主动推送。

能够看到 Protocal 值为 h2,这个就是http2的缩写。

这里有一个能够测试http2性能的网站 网站入口

能够看到,使用HTTP2的性能提高很是显著。

有些浏览器不支持http2,nginx会帮助浏览器作兼容处理,这个兼容方案为 ALPN ,客户端跟服务端会进行协商用哪一个协议,若是客户端只支持http1.1,服务端就会以http1.1的传输方式进行。

以上就是对http知识的总结,不少地方没有写全,有很差的地方请你们多多指正

相关文章
相关标签/搜索