最简单的例子
- 输入URL打开网页
- AJAX获取数据
- img标签加载图片
输入URL到打开网页的过程:
网络协议分层:
低三层:
- 物理层主要作用是定义物理设备如何传输数据(硬件上)
- 数据链路层在通信的实体间建立数据链路连接(软件上)
- 网络层为数据在结点之间传输创建逻辑链路
传输层:
- 向用户提供可靠的端到端(End-to-End)服务
- 传输层向高层屏蔽了下层数据通信的细节
应用层:
- 为应用软件提供了很多服务
- 构建于TCP协议之上
- 屏蔽网络传输相关细节
http协议的发展历史
HTTP1.1:Http 1.1详解
HTTP2:
- 所有数据以二进制传输
- 同一个连接里面发送多个请求不再按照顺序来
- 头信息压缩以及推送等提高效率的功能
HTTP的三次握手
因为http是不存在连接的概念,它只有请求和响应的概念,请求和响应都是数据包,它们之间是要通过一个传输的通道的,所以就使用TCP创建一个从客户端发起,服务端接受的连接。http就是在TCP连接的基础上发送请求的。
三次握手时序图:
三次握手主要是为了规避网络传输当中延迟而导致的服务器开销的问题。
URI、URL和URN
URI:Uniform Resource Identifier/统一资源标志符,用来唯一标识互联网上的信息资源,包括URL和URN
URL:Uniform Resource Locator/统一资源定位器,
URN:永久统一资源定位符
HTTP报文
HTTP方法:
- 用来定义对于资源的操作
- 常用的有GET、POST、PUT、DELETE等
- 从定义上讲有各自的语义
HTTP CODE
- 定义服务器对请求的处理结果
- 各个区间的CODE有各自的语义
1** : Hold on(代表这个操作需要持续进行)
2** : Here you go (代表这个操作是成功的)
3** : Go away (代表这个操作需要重定向)
4** : You fucked up (代表发送的这个请求有问题)
5** : I fucked up (代表服务器出现了错误)
具体的看这里(文档:HTTP CODE状态码)
- 好的HTTP服务可以通过CODE判断结果
创建一个简单的Web服务
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.end('123')
}).listen(8888)
console.log('server listening on 8888')
创建一个基于node.js的Web服务,然后在浏览器输出localhost:8888就可以得到返回的值了
认识HTTP客户端
这里我就没有一一说明了,可以看这篇文章 http客户端
CORS跨域请求的限制与解决
- 在要访问的服务器上放上这部分内容'Access-Control-Allow-Origin':'*'实现跨域请求
代码示例:
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
console.log('request come',request.url)
const html = fs.readFileSync('text.html','utf8')
response.writeHead(200,{
'Content-type':'text/html'
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.writeHead(200,{
'Access-Control-Allow-Origin':'*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ducoment</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET','http://127.0.0.1:8887/')
xhr.send()
</script>
</body>
</html>
其中server2.js中的'Access-Control-Allow-Origin':'*'使test.html使用server.js来跨域请求server2.js服务
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.writeHead(200,{
'Access-Control-Allow-Origin':'http://127.0.0.1:8888'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
如果服务器和发送请求的都是自己实现,这里就可以指定域名来访问服务器,从而实现安全的跨域请求 ,也可以通过判断request.url来进行不同的跨域请求设置
- 使用jsonp的方法来实现跨域请求
实现原理:浏览器允许link、img、script标签上面的src或者是ref跨域来实现的
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ducoment</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="http://127.0.0.1:8887/"></script>
</body>
</html>
直接在test.html中使用srcipt标签来实现跨域,这样就不需要在服务器设置Access-Control-Allow-Origin属性了
CORS跨域限制以及预请求验证
新建一个HTTP2文件夹,然后把之前文件夹的文件考到新建的这个文件夹
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
console.log('request come',request.url)
const html = fs.readFileSync('text.html','utf8')
response.writeHead(200,{
'Content-type':'text/html'
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.writeHead(200,{
'Access-Control-Allow-Origin':'*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ducoment</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
fetch('http://localhost:8887/',{
method: 'POST',
header: {
'X-Test-Core': '123'
}
})
</script>
<!-- <script src="http://127.0.0.1:8887/"></script> -->
</body>
</html>
这样分别运行server.js和server2.js服务,但是会报错
注意:
- 在跨域时允许的方法默认只有:GET、HEAD、POST,其他的方法是默认不允许的(其他方法就需要进行预请求)
- 允许Content-Type:text/plain、multipart/form-data、application/x-www-form-urlencoded,其他的Content-Type也需要预请求
- 其他限制:请求头限制(Fetch)、XMLHttpRequestUpload对象均没有注册任何事件监听器、请求中没有使用ReadableStream
什么情况下需要 CORS ?
跨域资源共享标准( cross-origin sharing standard )允许在下列场景中使用跨域 HTTP 请求:
- 前文提到的由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
- Web 字体 (CSS 中通过 @font-face 使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
- WebGL 贴图
- 使用 drawImage 将 Images/video 画面绘制到 canvas
- 样式表(使用 CSSOM)
CORS预请求:
- 跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
- 比如,站点 http://domain-a.com 的某 HTML 页面通过 <img> 的 src 请求 http://domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。
- 出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件。
跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest
或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。
具体可以看这篇文章:HTTP访问控制(CORS)
缓存Cache-Control
可缓存性:
- public(指在这个http请求返回的过程当中,在Cache-Control中设置了public这个值,代表在这个http请求中返回的内容所经过的任何路径<包括中间的一些http的代理服务器以及发出这个请求的客户端浏览器,它都可以对这个返回进行缓存的操作>)
- private(指只有发起请求的浏览器它才可以进行缓存)
- no-cache(指可以在本地进行缓存,也可以在proxy<代理>服务器缓存,但是每次你发起这个请求,都要去服务器哪里验证;如果服务器返回允许使用这部分缓存,你才可以使用缓存)
到期:
- max-age=<seconds>(设置这个缓存到多少秒才过期)
- s-maxage=<seconds>(s-maxage代替max-age,在代理服务器里才会生效,就是说在客户端浏览器不会读取这个属性)
- ma-stale=<seconds>(在max-age过期之后,如果我们返回的资源有这个max-stale的设置,max-stale是发起请求这一方,它主动带的一个头,作用就是即便max-age过期了,只要在max-stale这个时间内依然可以使用这个过期的缓存,而不需要去原服务器请求<注意:只有在发起端设置才有用>)
重新验证:
- must-revalidate(指在max-age过期了,我们必须到原服务端去发送这个请求来重新获取这部分数据,再来验证这部分内容是不是真的过期了,而不能直接使用本地的缓存)
- proxy-revalidate(用在缓存服务器的,指在缓存服务器过期之后,必须到原服务端去发送这个请求来重新获取这部分数据,再来验证这部分内容是不是真的过期了,而不能直接使用本地的缓存)
其他:
- no-store(指本地和代理服务器都是不可以存缓存的,就是每次都要去服务器端拿新的body内容的)
- no-transform(用在proxy服务器,就是告诉proxy服务器不可以随意改变返回的内容,例如不能随便压缩返回的图片)
注意:上面这些头只是一个限制性、声明性的作用,它没有任何强制的约束力
例子:新建一个Cache-Control文件夹
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
console.log('request come',request.url)
if(request.url === '/'){
const html = fs.readFileSync('text.html','utf8')
response.writeHead(200,{
'Content-type':'text/html'
})
response.end(html)
}
if(request.url === "/script.js") {
response.writeHead(200,{
'Content-type':'text/javascript',
'Cache-Control':'max-age=200'
})
response.end('console.log("script loaded twice")')
}
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.writeHead(200,{
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Headers':'X-Test-Cors',
'Access-Control-Allow-Methods': 'POST,PUT,Delete',
'Access-Control-Allow-Age': '1000'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ducoment</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="/script.js"></script>
</body>
</html>
但是有一个问题,我们希望服务端更新了内容之后,客户端可能更新不到了,因为客户端存储资源都是一些长缓存,那么服务端跟新了,客户端可能就还是读取的客户端的缓存?
解决方案:在我们打包完成后,会在实际打包完成的js文件名上根据内容的结果加上一段hash码,这段hash码是根据打包完成的js文件以及其他的静态资源文件,它们的内容进行一个hash计算,所有js文件或者静态资源文件内容不变,那个hash码就不会变。也就是当url没有变就会使用本地的静态资源缓存,如果所有js文件或者静态资源文件内容变化了,那么嵌入到这些资源的url路径的hash码就会发生变化,这样客户端就会发起新的请求
资源验证
上面就是资源请求的一个过程
验证头:
- Last-Modified(上次修改时间,配合If-Modifie-Since或者If-Unmodified-Since使用,就是说请求的资源有Last-Modified,那么浏览器在下次请求的时候就会带上Last-Modified,通过If-Modifie-Since或者If-Unmodified-Since把这个请求头带到服务器上,然后服务器就可以读取Header中If-Modifie-Since的值来对比资源存在的地方,最后来对比上次修改的时间,如果时间是一样的,代表资源还没有被重新修改过,服务器就告诉浏览器可以直接使用这个缓存<比对上次修改时间以验证资源是否需要更新>)
- Etag(数据签名,就是说资源发生了改变,它的数据签名就会改变;然后下一次浏览器发起请求的使用,这个数据签名就会配合If-Match或者If-Non-Match使用,再对比资源的签名判断是否使用缓存)
代码:
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
console.log('request come',request.url)
if(request.url === '/'){
const html = fs.readFileSync('text.html','utf8')
response.writeHead(200,{
'Content-type':'text/html'
})
response.end(html)
}
if(request.url === "/script.js") {
response.writeHead(200,{
'Content-type':'text/javascript',
'Cache-Control':'max-age=20000000,no-cache',
'Last-modified':'123',
'Etag':'777'
})
const etag = request.headers['if-none-match']
if (etag === '777') {
response.writeHead(304,{
'Content-type':'text/javascript',
'Cache-Control':'max-age=20000000,no-store',
'Last-modified':'123',
'Etag':'777'
})
response.end('123')
} else {
response.writeHead(200,{
'Content-type':'text/javascript',
'Cache-Control':'max-age=20000000,no-store',
'Last-modified':'123',
'Etag':'777'
})
response.end('console.log("script loaded twice")')
}
}
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer(function(request, response){
console.log('request come',request.url)
response.writeHead(200,{
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Headers':'X-Test-Cors',
'Access-Control-Allow-Methods': 'POST,PUT,Delete',
'Access-Control-Allow-Age': '1000'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Ducoment</title>
</head>
<body>
<script src="/script.js"></script>
</body>
</html>
启动server.js(node server.js),然后在浏览器监听这个端口,再查看notwork可以发现在资源验证中设置了no-store,每次请求资源都是重新发起的请求,没有缓存,如果去掉no-store设置,第二次请求就会使用缓存中的资源
Cookie和Session
cookie:在服务端返回数据额时候,通过Set-Cookie这个header设置在浏览器里面,它保存在浏览器里面的内容,这个内容就叫它cookie;浏览保存了这个cookie之后,它在下次同域的请求当中,它就会带上这个cookie,通过这样来保证这个内容就是这个用户的
特点:
- 通过Set-Cookie设置
- 下次请求自动带上
- 键值对,可以设置多个
属性:
- max-age和expires设置过期时间
- Secure(指只在https的时候发送)
- HttpOnly(设置无法通过document.cookie访问,安全性考虑)
代码:
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
console.log('request come',request.url)
const host = request.headers.host
if(request.url === '/'){
const html = fs.readFileSync('text.html','utf8')
if(host === 'a.test.com') {
response.writeHead(200,{
'Content-type':'text/html',
'Set-Cookie':['id=123;max-age=2','abc=345;HttpOnly']
})
}
response.end(html)
}
}).listen(8888)
console.log('server listening on 8888')
test.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Ducoment</title>
</head>
<body>
<div>
Content
</div>
<script>
console.log(document.cookie)
</script>
</body>
</html>
还有就是不同域名的cookie是不能共享的,不过我们可以同时设置domain来实现所有的二级域可以读到一级域的cookie
if(host === 'test.com') {
response.writeHead(200,{
'Content-type':'text/html',
'Set-Cookie':['id=123;max-age=2','abc=345;domain=test.com']
})
}
注意:host === 'test.com'中改变成test.com,因为不可以跨域设置domain
session:
作用:服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。
Session和Cookie的主要区别:
- Cookie是把用户的数据写给用户的浏览器。
- Session技术把用户的数据写到用户独占的session中。
- Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
HTTP长连接
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:
Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。 HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
Connection:keep-alive的作用:是可以复用TCP/IP的链接,不用每次发起一个请求就创建一个TCP/IP的链接(只有在同域下,这个TCP/IP的链接才可以复用)
相反,Connection:close:就是每次发起请求就会创建一个TCP/IP链接,没有重复使用TCP/IP链接(每个连接的建立都是需要资源消耗和时间消耗的。)
长连接短连接操作过程
短连接的操作步骤是:
建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接
长连接的操作步骤是:
建立连接——数据传输...(保持连接)...数据传输——关闭连接
参考文章:HTTP长连接、短连接
数据协商
数据协商:在客户端发送给服务端一个请求时,客户端会声明我希望这个请求拿到的数据格式以及数据相关的一些限制,服务端会根据这个请求里面表示的客户端想要拿到的数据来进行判断,服务端会根据这个判断,就可能会返回不同的数据(服务端是根据客户端发送的请求头信息来进行区分,然后服务端决定返回怎么样的数据)
分类:
- 请求
- 返回
Accept:
- Accept(表示想要的数据类型)
- Accept-Encoding(代表这个数据使用什么样的编码方式进行传输)
- Accept-Language(实现不同的地方访问服务展示的语言不同)
- User-Agent(表示浏览器相关的一些信息,手机端的浏览器和PC端的浏览器是不一样的,就可以通过User-Agent判断)
Content:
- Content-Type(对应Accept,Accept可以接收几种数据格式,Content-Type从其中的数据格式中选择一种作为真正返回的数据格式)
Content-Type一般有这三种:
application/x-www-form-urlencoded:数据被编码为名称/值对。这是标准的编码格式。
multipart/form-data: 数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
text/plain: 数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符。
- Content-Encoding(对应Accept-Encoding)
- Content-language(根据你的请求返回语言还是没有根据请求返回语言)
当你想要设置你想要的数据时就通过设置Accept中的属性来达到,服务端就会根据你的请求来进行对应的请求
Redirect
Redorect:就是我们通过 url去访问一个路径,请求这个资源的时候,发现这个资源已经不在这个url指定的位置了,这时候服务器就会告诉客户端浏览器,请求的资源现在在那个地方,浏览器再去重新请求那个地方,这样客户端浏览器就去拿到那个资源了
这个需要服务端来设置,通过服务器判断你请求的路径来实现重定向到新的url,一般返回3**有几种状态:
- 302(每次访问都需要通过旧的url再跳转到新的资源的URL上)
- 301(指定你之前的资源的路径永久变成了新定义的资源路径,这个头就会告诉浏览器,出现了之前路径访问的时候,直接在浏览器就会变成新的路径,就不需要服务器去指定一个新的location,因为它是一个永久的变更;除非注定清除缓存,不然浏览器都会默认调整的)
Content-Security-Policy
就是内容安全策略,为了让我们的网站变得更加的安全
作用:
- 限制资源获取(限制网页当中各种资源获取的情况,就是从哪里去获取,请求发送到哪个地方)
- 报告资源获取越权(就是网页获取了一些我们不应该获取的资源,然后就给服务进行报告)
限制方式:
- default-src限制全局(限制全局的,跟链接有关的,都可以限制它的作用范围
- 制定资源类型(可以根据特定的资源来限制它的资源范围)
资源类型(网页上跟链接有关的,需要从外链进行加载内容,都可以进行资源类型的限制)
- connect-src(请求发向的地方目标,可以进行限制)
- mainfest-src
- img-src(图片可以从哪几个网站加载,进行限制)
- font-src
- media-src
- style-src(样式从哪几个网址加载,进行限制)
- frame-src
- script-src(脚本从哪几个网址加载,进行限制)
- ...
这个内容安全策略:可以直接强制限制加载,也可以允许加载只report(report-only)
Nginx安装和基础代理配置
Nginx的作用: 通过一台物理机器可以跑多个不同的服务,节省很多的资源,从而高效地利用计算机
Nginx下载页面,window用户直接下载这个页面上的windows压缩包,然后解压就可以使用了
解压出来可以在解压的文件夹中打开git bash来运行nginx,
然后使用浏览器访问localhost,如果出现下面的页面则允许成功了
基础代理配置:
在nginx软件下新建一个servers并创建一个test.conf文件,并且修改nginx.conf,引入test.conf文件:
test.conf:
server {
listen 80;
server_name test.com;
location /
{
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
nginx.conf(在nginx软件下的conf文件夹下的nginx.conf): 在http下新增下面的配置,来引入自己的nginx配置
include servers/*.conf;
通过启动对应的server服务,
server.js:
const http = require('http')
const fs = require('fs')
const zlib = require('zlib')
http.createServer(function (request, response) {
console.log("request come", request.headers.host)
const html = fs.readFileSync('test.html')
response.writeHead(200, {
'Content-Type': 'text/html',
// 'X-Content-Options': 'nosniff'
'Content-Encoding': 'gzip'
})
response.end(zlib.gzipSync(html))
}).listen(8888)
console.log('server listening on 8888')
tset.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/form" id="form" enctype="application/x-www-form-urlencoded">
<input type="text" name="name">
<input type="password" name="password">
<input type="file" name="file">
<input type="submit">
</form>
<script>
var form = document.getElementById('form')
form.addEventListener('submit', function (e) {
e.preventDefault()
var formData = new FormData(form)
fetch('/form', {
method: 'POST',
body: formData
})
})
</script>
</body>
</html>
然后通过test.com来访问代理的网址:
可能会报错:
1.域名没有解析到本地吧,你要在hosts文件里面配置一下,还有先确认一下127.0.0.1:80能不能访问
2.因为没有定义的域名没有配置 DNS 解析,打开 C:\Windows\System32\drivers\etc\hosts 文件,在最后写上 # 如果只在本机访问,新增IP改为127.0.0.1 www.test.com
Nginx代理配置和代理缓存的用处
作用:代理缓存是在代理设置的,所有每一个新请求都会经过代理,如果代理缓存已经缓存了一次。在浏览器客户端,只要第一个用户请求了,代理服务器缓存之后,其他用户都可以使用代理服务器的缓存
修改test.conf:
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80;
server_name test.com;
location /
{
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
server {
listen 80;
server_name a.test.com;
location /
{
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
新建一个文件夹,并创建server.js和test.html文件
server.js:
const http = require('http')
const fs = require('fs')
const wait = (seconds) => {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000)
})
}
http.createServer(function (request, response) {
console.log('request come', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}
if (request.url === '/data') {
response.writeHead(200, {
'Cache-Control': 'max-age=2, s-maxage=20, private',
'Vary': 'X-Test-Cache'
})
wait(2).then(() => response.end('success'))
}
}).listen(8888)
console.log('server listening on 8888')
test.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>This is content, and data is: <span id="data"></span></div>
<button id="button">click me</button>
</body>
<script>
var index = 0
function doRequest () {
var data = document.getElementById('data')
data.innerText = ''
fetch('/data', {
headers: {
'X-Test-Cache': index++
}
}).then(function (resp) {
return resp.text()
}).then(function (text) {
data.innerText = text
})
}
document.getElementById('button').addEventListener('click', doRequest)
</script>
</html>
注意,可以在server.js修改对应的返回头信息来实现各种配置,查看这个“缓存Cache-Control”章节
头信息:
- Vary(指定在发生一个请求的时候,只要和服务器端的指定的http头的值相同的情况下,客户端才会使用缓存)
https
定义:http+ sockets = https,就变成了安全的https。也就意味着http是不安全的,因为http协议传输的数据都是未加密的,也就是明文的。为了保证这些隐私数据能加密传输,于是根据ssl(Secure Sockets Layer)协议用于对http协议传输的数据进行加密,从而就诞生了https。
加密:
- 私钥(放在服务器上,用于公钥加密过的数据)
- 公钥(放在互联网上,所有人都能拿到的一串加密的字符串,这个加密的字符串是来加密我们的字符信息的。当加密的数据传到服务器上,只有服务器通过私钥解密,才能把公钥加密的数据拿出来)
公钥和私钥主要是用在握手的时候进行一个传输,然后握手的时候,公钥和私钥传输的内容,实际上是真正传输的过程中加密的字符串。
客户端和服务端就可以通过这个加密的字符串进行安全的数据传输
https握手的过程:
后期就通过主迷钥来进行客户端和服务端的数据传输,所以中间的加密数据传输就是安全的数据传输
客户端生成Random(随机数)和Cipher Suites(支持的加密套件)发送到服务端,然后服务端选择其中的一种加密套件Cipher Suite和Random(随机数)来返回客户端,同时服务端还会继续返回来告诉客户端,服务端的公钥(证书)是怎样的。最后客户端就通过公钥来加密数据并把数据传输给服务端,服务端通过私钥解密来获取解密的数据。
https和http的主要区别:
一、https协议需要到ca机构申请ssl证书(如沃通CA),另外沃通CA还提供3年期的免费ssl证书,高级别的ssl证书需要一定费用。
二、http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
三、http和https使用的是完全不同的连接方式,用的端口也不一样,http是80端口,https是443端口。
四、http的连接很简单,是无状态的;https协议是由ssl+http协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
使用Nginx部署https的服务
在Nginx的软件一级目录下新建一个certs文件夹,并在里面打开git bash,输入以下代码:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-privket.pem -out localhost-cert.pem
修改test.conf配置:
代码:
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 443;
server_name test.com;
listen ... ssl;
ssl_certificate_key ../certs/localhost-privket.pem;
ssl_certificate ../certs/localhost-cert.pem;
location /
{
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
这样会报错,访问https时候正常,不过直接访问的时候会出现400 Bad Request The plain HTTP request was sent to HTTPS port错误
需要再修改test.conf文件:
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80;
server_name test.com;
listen 443 ssl;
ssl_certificate_key ../certs/localhost-privket.pem;
ssl_certificate ../certs/localhost-cert.pem;
location /
{
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
最后就可以通过https来进行数据传输了
还可以通过配置来实现访问http协议的网址时自动转到https协议上,修改test.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 {
server_name test.com;
listen 443 ssl;
ssl_certificate_key ../certs/localhost-privket.pem;
ssl_certificate ../certs/localhost-cert.pem;
location /
{
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
还有其他Nginx配置,可以查看这个网站:Nginx官方文档
http2的优势和Nginx配置http2的简单使用
http1.1(http2)的优势:
- 信道复用
- 分帧传输(传输数据不需要连续的发送,可以分成不同的帧进行发送)
- Server Push(使服务端可以主动的向客户端发送一些内容,实现并行的向客户端返回数据)
Nginx配置http2的简单使用:
注意:需要支持https才能使用http2,通过Nginx来开启http2
修改对应的test.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 ../certs/localhost-privket.pem;
ssl_certificate ../certs/localhost-cert.pem;
location /
{
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;
}
}
新建http2文件夹,并新建以下文件:
server.js:
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
const html = fs.readFileSync('test.html', 'utf8')
const img = fs.readFileSync('test.jpg')
if (request.url === '/') {
response.writeHead(200, {
'Content-Type': 'text/html',
'Connection': 'keep-alive',
'Link': '</test.jpg>; as=image; rel=preload'
})
response.end(html)
} else {
response.writeHead(200, {
'Content-Type': 'image/jpg',
'Connection': 'keep-alive' // or close
})
response.end(img)
}
}).listen(8888)
console.log('server listening on 8888')
test.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<img src="/test.jpg" alt="">
</body>
</html>
test.jpg:
运行server.js,并启动nginx,结果如下:
总结
- HTTP原理
- HTTP技术点
- Nginx实践、面向未来的HTTP