HTTP经典五层模型
低三层
- 物理层:主要作用是定义物理设备如何传输数据
- 数据链路层:在通信的实体间建立数据链路连接
- 网络层:为数据在结点之间传输创建逻辑链路
传输层
- 向用户提供可靠的端到端(End-to-End)服务
- 传输层向高层屏蔽了下层数据通信的细节
应用层
- 为应用软件提供了很多服务
- 构建于TCP协议之上
- 屏蔽网络传输相关细节
HTTP三次握手
TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。
三次握手
当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。
TCP三次握手的过程如下:
- 客户端发送SYN(SEQ=x)报文给服务器端,进入请求连接(SYN_SEND)状态。
- 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。SYN_RECV是指,服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再
- 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入连接(Established)状态。
URI、URL、URN
URI
Uniform Resource Identifier/统一资源标志符
用来唯一标识互联网上的信息资源。URI包含URL和URN
如http://www.baidu.com
URL
Uniform Resource Locator/统一资源定位器
http://user:pass@host.com:80/path?query=string#hash,此类格式的都叫做URL,比如ftp协议
URN
- 永久统一资源定位符
- 在资源移动之后还能被找到
- 目前还没有非常成熟的使用方案
HTTP报文
HTTP方法
- 用来定义对于资源的操作
- 常用有GET、POST等
- 从定义上讲有各自的语义
HTTP CODE
- 定义服务器对请求的处理结果
- 各个区间的CODE有各自的语义
- 好的HTTP服务可以通过CODE判断结果
创建一个简单的Web服务
const http = require('http')
http.createServer((req, res) => {
console.log('req come', req.url)
res.end('123')
}).listen(8888)
console.log('开启成功8888')
CORS跨域请求的限制与解决
-
server1
const http = require('http') const fs = require('fs') http.createServer((req, res) => { console.log('req come', req.url) const html = fs.readFileSync('text.html', 'utf8') res.writeHead(200, { 'Content-Type': 'text/html' }) res.end(html) }).listen(8888) console.log('8888服务器开启成功')
-
server2
const http = require('http') http.createServer((req, res) => { console.log('req come', req.url) res.end('123') }).listen(8887) console.log('8887服务器开启成功')
-
text.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> <script> var xhr = new XMLHttpRequest() xhr.open('GET', 'http://127.0.0.1:8887/') xhr.send() </script> </body> </html>
此时8888服务器提供了text.html的服务,但是页面内容发送了一个8887的请求,那么会出现跨域问题。如图
因为没有设置服务端允许跨域的头,接下来我们只要在server2中设置一下头就可以解决跨域问题了。
const http = require('http')
http.createServer((req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": '*'
})
res.end('123')
}).listen(8887)
console.log('8887服务器开启成功')
CORS跨域限制以及预请求验证
允许方法
- GET
- HEAD
- POST
允许Content-Type
- text/ plain
- multipart/form-data
- application/x-www-form-urlencoded
其他限制
- 请求头限制
- XMLHttpRequestUpload对象均没有注册任何事件监听器
- 请求中没有使用ReadableStream对象
预请求
const http = require('http')
http.createServer((req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": '*',
//设置允许的请求头
'Access-Control-Allow-Headers': 'X-Test-Cors ',
//设置允许的请求方法
'Access-Control-Allow -Methods': ' POST, PUT , Delete',
//设置最长时间
'Access-Control-Max -Age': '1000'
})
res.end('123')
}).listen(8887)
console.log('8887服务器开启成功')
缓存Cache-Control
可缓存性
- public:任何代理服务器都可以对数据进行缓存
- private:只有发起请求的浏览器可以缓存
- no-cache:任何一个节点都不可以缓存
到期
-
max-age=
- 设置时间过期,过期后再次发送请求到服务器请求信内容
-
s-maxage=
- 在代理服务器里才生效
-
max-stale=
- 在max-stale时间内,即使max-age时间过期,也可以使用(
浏览器用不到
)
- 在max-stale时间内,即使max-age时间过期,也可以使用(
重新验证
- must-revalidate:如果缓存已经过期,需要向源服务端重新获取数据,不能直接使用
- proxy-revalidate:缓存服务器必须在过期时在源服务器重新请求
其他
- no-store:本地和代理不可以缓存
- no-transform:不可以随便改动返回的内容
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
console.log('req come', req.url)
if (req.url === '/') {
const html = fs.readFileSync('text.html', 'utf8')
res.writeHead(200, {
'Content-Type': 'text/html'
})
res.end(html)
}
if (req.url === '/script.js') {
res.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': ' max-age=200'
})
res.end('console.log("script loaded")')
}
}).listen(8888)
console.log('8888服务器开启成功')
缓存验证Last-Modified和Etag的使用
Last-Modified
- 上次修改时间
- 配合If-Modified- Since或者If-Unmodified-Since使用
- 对比上次修改时间以验证资源是否需要更新
Etag
- 数据签名
- 配合If-Match或者If-Non-Match使用
- 对比资源的签名判断是否使用缓存
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
console.log('req come', req.url)
if (request.url === '/script.js') {
console.log(request.headers)
const etag = request.headers('if-none-match')
if (etag === '777') {
response.writeHead(304, {
'Content-Type': 'text/javascript',
'Cache-Control': ' max-age=200000,no-cache',
'Last-Modified': '123',
"Etag": '777'
})
response.end('456')
} else {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': ' max-age=200000,no-cache',
'Last-Modified': '123',
"Etag": '777'
})
response.end('console.log("script loaded")')
}
}
}).listen(8888)
console.log('8888服务器开启成功')
Cookie和Session
Cookie
- 通过Set-Cookie设置
- 下次请求会自动带上
- 键值对,可以设置多个
Cookie属性
- max-age和expires设置过期时间
- Secure只在https的时候发送
- HttpOnly无法通过document.cookie访问
const http = require('http')
const fs = require('fs')
http.createServer((req, res) => {
console.log('req come', req.url)
if (request.url === '/script.js') {
const html = fs.readFileSync('text.html', 'utf8')
res.writeHead(200, {
'Content-type': 'text/html',
'Set-Cookie': ['id=123;max-age=2', 'abc=456;HttpOnly']
})
res.end(html)
}
}).listen(8888)
console.log('8888服务器开启成功')
HTTP长连接
- HTTP1.1中有6个并发的链接
- HTTP2在一个TCP链接可以并发的发送多个请求
const http = require('http')
http.createServer(function (request, response) {
response.writeHead(200, {
'Content-Type': ' text/html',
"Connection": "close",
"Connection": "keep-alive"
})
}).listen(8887)
console.log('8887服务器开启成功')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img src="../1.png">
<img src="../2.png">
<img src="../3.png">
<img src="../4.png">
<img src="../5.png">
<img src="../6.png">
</body>
</html>
数据协商
- 请求(客户端)
- Accept
- 明确想要接收数据的类型
- Accept-Encoding
- 明确编码方式,限制服务端压缩方式
- Accept-Language
- 返回信息判断是中文还是英文
- User-Agent
- 判断浏览器相关信息
- Accept
- 返回(服务端)
- Content-Type
- 选择一种数据格式,作为真正返回的数据格式进行返回(申明)
- Content-Encoding
- 对应Accept-Encoding
- Content-Language
- 对应Accept-Language
- Content-Type
Redirect
- 301:永久跳转(变更),除非清除浏览器缓存
- 302:临时跳转
const http = require("http");
const fs = require("fs");
http.createServer(function (req, res) {
console.log("request come", req.url);
if (req.url === "/") {
res.writeHead(302, {
Location: "/new",
});
res.end("");
} else {
res.writeHead(200, {
"Content-Type": "text/html",
});
res.end("<div>this is content</div>");
}
}).listen(8888);
console.log('8888服务器开启成功')
CSP
全称为Content-Security-Policy
,译为内容安全策略
作用
- 限制资源获取
- 报告资源获取越权
限制方式
- default-src限制全局
- 资源类型
- connect-src
- 请求发向的目标的限制
- font-src
- 策略可以包含一个或多个源
- frame-src
- 可以允许一个或多个源
- media-src
- 可以允许一个或多个源
- manifest-src
- 可以允许一个或多个源
- img-src
- 图片可以从哪些网址上加载
- style-src
- 样式和脚本的资源从哪些网址上加载
- script-src
- 样式和脚本的资源从哪些网址上加载
- 。。。
- connect-src
实例
-
限制内联js
const http = require("http"); const fs = require("fs"); http.createServer(function (req, res) { console.log("request come", req.url); if (req.url === '/') { const html = fs.readFileSync('text.html', 'utf8') res.writeHead(200, { 'Content-Type': ' text/html', "Content-Security-Policy": "default-src http: https: " }) res.end(html) } else { res.writeHead(200, { 'Content-Type': ' application/javascript', }) req.end('console.log("loaded script")') } }).listen(8888); console.log('8888服务器开启成功')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div>This is content</div> <script> console.log('inline js') </script> <script src="script.js"></script> </body> </html>
-
限制只能加载某个网站的脚本,不写就是本站
const http = require("http"); const fs = require("fs"); http.createServer(function (req, res) { console.log("request come", req.url); if (req.url === '/') { const html = fs.readFileSync('text.html', 'utf8') res.writeHead(200, { 'Content-Type': ' text/html', "Content-Security-Policy": " default-src\'self\' " }) res.end(html) } else { res.writeHead(200, { 'Content-Type': ' application/javascript', }) req.end('console.log("loaded script")') } }).listen(8888); console.log('8888服务器开启成功')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div>This is content</div> <script> console.log('inline js') </script> <script src="script.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> </body> </html>
-
只能限制表单跳转本站
const http = require("http"); const fs = require("fs"); http.createServer(function (req, res) { console.log("request come", req.url); if (req.url === '/') { const html = fs.readFileSync('text.html', 'utf8') res.writeHead(200, { 'Content-Type': ' text/html', "Content-Security-Policy": "default-src \'self\'; form-action \'self\'" }) res.end(html) } else { res.writeHead(200, { 'Content-Type': ' application/javascript', }) req.end('console.log("loaded script")') } }).listen(8888); console.log('8888服务器开启成功')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div>This is content</div> <form action="http://baidu.com"> <button type="submit">点我</button> </form> <script> console.log('inline js') </script> <script src="script.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script> </body> </html>
Nginx代理以及面向未来的HTTP
Nginx的下载安装
直接去官网下载Nginx下载官网
启动nginx
- 可以用Git Bash启动(或者用命令行启动也行)
- 找到指定文件夹下,输入命令
nginx.exe
或者start nginx
,回车即可
关闭nginx
- 输入nginx命令:
nginx -s stop
(快速停止nginx) 或nginx -s quit
(完整有序的停止nginx) - 使用taskkill:
taskkill /f /t /im nginx.exe
Nginx基础代理配置
通过 include server/*.conf 来实现一个站点设置配置文件
在nginx.conf文件中include来引入其他的conf文件,实际上就是代码写在那里面,只是方便后期管理而已。
server{
listen 80;
server_name test.com;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host;#代理服务器去修改头的功能
}
}
浏览器发送的请求是发送到nginx的,nginx转发,即nginx发起请求。
Nginx代理配置和代理缓存的用处
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;#代理服务器去修改头的功能
}
}
HTTPS
加密
- 公钥:用于加密会话密钥、验证数字签名,或加密可以用相应的私钥解密的数据
- 私钥:单个私钥来加密和解密数据
HTTP2
优势
- 信道复用
- 分帧传输
- Server Push
http1与http2的比较