- Access-Control-Allow-Origin 访问-控制-允许-来源
- CORS Cross-Origin Resource Sharing 跨源资源共享
# CORS预请求
- 跨域请求的允许方法(get/post/head),其他的方法默认不允许
- 跨域请求允许的Content-Type(text/plain mulitpart/form-data application/x-www-form-urlencoded)
如果是自定义的请求头,需要配置Access-Control-Allow-Headers
"Access-Control-Allow-Headers":"xx-xxx"
如果要使用其他请求方法需要配置Access-Control-Allow-Methods
"Access-Control-Allow-Methods":"POST,PUT,Delete"
时间方面
"Access-Control-Max-Age":"1000"
const http = new require("http")
http.createServer(function(req,res){
res.writeHead(200,{
'Access-Control-Allow-Origin':"*",
"Access-Control-Allow-Methods":"POST,PUT,Delete",
"Access-Control-Allow-Headers":"x-1,x-3",
"Access-Control-Max-Age":"1000"
})
res.end('99999')
}).listen(9001,()=>{
console.log('server is ok')
})
fetch('http://localhost:9001',{
method:"PUT",
headers:{
'x-1':"123",
"x-3":'1111'
}
})
# 缓存Cache-Control
- 可缓存性
public private(只有浏览器缓存才有效,代理服务器即使做了缓存也不会生效) no-cache(需要缓存验证) no-store(不缓存,不管有没有设置到期时间,都回去请求数据)
- 到期
max-age=<seconds>
s-maxage=<seconds>//为代理服务器设置
max-stale
if(req.url==="/s.js"){
res.writeHead(200,{
"Content-Type":"text/javascript",
'Cache-Control':"max-age=20"
})
res.end('console.log("apple1")')
}
- 重新验证
revalidate:使重新生效;
must-revalidate
proxy-revalidate
- Last-Modified
上次修改时间,配合If-Modified-Since或者If-Unmodified-Since使用
- Etag
数据签名,配合If-Match或者If-None -Match使用
if (request.url==='/script.js') {
const etag=request.headers['if-none-match']
if (etag==='777') {
response.writeHead(304,{
'Content-Type':'text/javascript',
'Cache-Control':'max-age=30000000,no-cache',
'Last-Modified':'123',
'Etag':'777'
})
response.end('')
}else{
response.writeHead(200,{
'Content-Type':'text/javascript',
'Cache-Control':'max-age=30000000,no-cache',
'Last-Modified':'123',
'Etag':'777'
})
response.end('console.log("script loaded!")')
}
};
# cookie
res.writeHead(200,{
'Content-Type':'text/html',
'Set-cookie':['id=1234;max-age=2','name=sk','sex=0;HttpOnly',"cc=1990;domain=test.com"]
})
# 长连接
if (request.url==='/') {
const html=fs.readFileSync('test.html','utf8')
response.writeHead(200,{
'Content-Type':'text/html',
'Set-Cookie':['id=123;max-age=10','abc=456;HttpOnly','test=789;domain=test.com']
})
response.end(html)
}else{
response.writeHead(200,{
'Content-Type':'image/jpg',
'Connection':'close'
})
response.end(img)
}
http1.1默认长连接,可以设置connection来决定是否进行长连接,close为关闭,keep-alive也可以设置时长
# 数据协商
const http=require('http');
const fs=require('fs')
const zlib=require('zlib')
http.createServer(function(request,response){
const html=fs.readFileSync('test.html')
response.writeHead(200,{
'Content-Type':'text/html',
'Content-Encoding':'gzip',
'X-Content-Type-Options':'nosniff'
})
response.end(zlib.gzipSync(html))
// response.writeHead(200,{
// 'Content-Type':'text/html',
// 'X-Content-Type-Options':'nosniff'
// })
// response.end(html)
}).listen(8888)
console.log('server listening on 8888')
# Redirect
if (request.url==='/') {
//302:临时跳转(永远都在服务器端做跳转)
//301:永久跳转(告诉浏览器下次访问直接在浏览器端做跳转,301使用要非常慎重,如果用户不清浏览器缓存会一直跳转)
response.writeHead(302,{
'Location':'/new'
})
response.end('')
};
if (request.url==='/new') {
response.writeHead(200,{
'Content-Type':'text/html',
'Content-Encoding':'gzip',
'X-Content-Type-Options':'nosniff'
})
response.end(zlib.gzipSync(html))
};
# Content-Security-Policy
HTTP 响应头 Content-Security-Policy 允许站点管理者控制用户代理能够为指定的页面加载哪些资源。除了少数例外情况,设置的政策主要涉及指定服务器的源和脚本结束点。这将帮助防止跨站脚本攻击(Cross-Site Script)
Content-Security-Policy: <policy-directive>; <policy-directive>
// 部分指令
+ default-src:为其他取指令提供备用服务
+ font-src:设置允许通过 @font-face 加载的字体源地址
+ frame-src:设置允许通过类似 <frame> 和 <iframe> 标签加载的内嵌内容的源地址
+ img-src:限制图片和图标的源地址
+ script-src:限制 JavaScript 的源地址
+ style-src:限制层叠样式表文件源
+ form-action:限制能被用来作为给定上下文的表单提交的目标 URL(说白了,就是限制 form 的 action 属性的链接地址)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 前端页面上设置,加了jquery就访问不了了-->
<meta http-equiv="Content-Security-Policy" content="form-action 'self';script-src 'self' http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js https://unpkg.com/vue@3/dist/vue.global.js ;">
</head>
<body>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
console.log($)
</script>
<img src="https://weishell.github.io/img/logo.jpg" alt="">
</body>
</html>
if (request.url==='/') {
response.writeHead(200,{
'Content-Type':'text/html',
// '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':'acript-src \'self\'; form-action \'self\';'
// 'Content-Security-Policy':'acript-src \'self\'; form-action \'self\'; report-uri /report'
'Content-Security-Policy-Report-Only':'acript-src \'self\'; form-action \'self\'; report-uri /report'
})
response.end(html)
}
# 代理服务器使用缓存
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; #nginx对http请求的host进行转发
}
}
server {
listen 80;
server_name a.test.com;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host; #nginx对http请求的host进行转发
}
}
server {
listen 80;
server_name b.test.com;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header Host $host; #nginx对http请求的host进行转发
}
}
const http=require('http');
const fs=require('fs')
const wait=(second)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},second*1000)
})
}
http.createServer(function(request,response){
console.log('request come',request.headers.host)
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':'s-maxage=200',
'Vary':'X-Text-Cache'
})
wait(2).then(()=>{
response.end('success')
})
};
}).listen(8888)
console.log('server listening on 8888')
<script type="text/javascript">
var index=0;
function doRequest(){
document.getElementById('data').innerText=''
fetch('/data',{
headers:{
'X-Text-Cache':index++
}
}).then((resp)=>{
return resp.text()
}).then((text)=>{
document.getElementById('data').innerText=text
})
}
document.getElementById('btn').addEventListener('click',doRequest)
</script>
Vary:当设置的值相等时才会使用缓存 ;可以不需要这个参数;s-maxage专为代理服务器配置的缓存时间;
问题
- 现代浏览器在与服务器建立了一个 TCP 连接后是否会在一个 HTTP 请求完成后断开?什么情况下会断开?
在 HTTP/1.0 中,一个服务器在发送完一个 HTTP 响应后,会断开 TCP 链接。但是这样每次请求都会重新建立和断开 TCP 连接,代价过大。所以虽然标准中没有设定,某些服务器对 Connection: keep-alive 的 Header 进行了支持。意思是说,完成这个 HTTP 请求之后,不要断开 HTTP 请求使用的 TCP 连接。这样的好处是连接可以被重新使用,之后发送 HTTP 请求的时候不需要重新建立 TCP 连接,以及如果维持连接,那么 SSL 的开销也可以避免.头一次访问,有初始化连接和 SSL 开销。初始化连接和 SSL 开销消失了,说明使用的是同一个 TCP 连接。
持久连接:既然维持 TCP 连接好处这么多,HTTP/1.1 就把 Connection 头写进标准,并且默认开启持久连接,除非请求中写明 Connection: close,那么浏览器和服务器之间是会维持一段时间的 TCP 连接,不会一个请求结束就断掉。
所以第一个问题的答案是:默认情况下建立 TCP 连接不会断开,只有在请求报头中声明 Connection: close 才会在请求完成后关闭连接。
- 一个 TCP 连接可以对应几个 HTTP 请求?
如果维持连接,一个 TCP 连接是可以发送多个 HTTP 请求的
- 一个 TCP 连接中 HTTP 请求发送可以一起发送么(比如一起发三个请求,再三个响应一起接收)?
HTTP/1.1 存在一个问题,单个 TCP 连接在同一时刻只能处理一个请求,意思是说:两个请求的生命周期不能重叠,任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠.在建立起一个 TCP 连接之后,假设客户端在这个连接连续向服务器发送了几个请求。按照标准,服务器应该按照收到请求的顺序返回结果,假设服务器在处理首个请求时花费了大量时间,那么后面所有的请求都需要等着首个请求结束才能响应。但是,HTTP2 提供了 Multiplexing 多路传输特性,可以在一个 TCP 连接中同时完成多个 HTTP 请求。
为什么有的时候刷新页面不需要重新建立 SSL 连接? TCP 连接有的时候会被浏览器和服务端维持一段时间。TCP 不需要重新建立,SSL 自然也会用之前的。
浏览器对同一 Host 建立 TCP 连接到数量有没有限制? 有。Chrome 最多允许对同一个 Host 建立六个 TCP 连接。不同的浏览器有一些区别。