node学习http-header

995 阅读5分钟

304

last-modified etag服务端设置 if-modified-since if-none-match 客户端 expires cache-control 200 强制缓存

gzip

accept-encoding 浏览器来的头

content-encoding 服务端设置

referrer referer

referer浏览器默认的

代表的资源的来源 (可以做安全判断) 可以在服务端用当前资源的host 和 referer 做对比如果不一致 说明这个资源就被其他网址所引用

如果你就想用别人 你可以把图片爬取下来,在去使用

/usr/hosts C:\Windows\System32\drivers\etc

const http = require('http');
const path = require('path');
const fs = require('fs');
const url = require('url');
const whiteList = ['a.zf.cn:3000'] //白名单
const server = http.createServer((req,res)=>{
    const {pathname} = url.parse(req.url,true);
    const absPath = path.join(__dirname,pathname);
    fs.stat(absPath,function (err,statObj) {
        if(err){
            res.statusCode = 404;
            res.end('Not Found');
            return
        }
        if(statObj.isFile()){
            // 只有别人引用这张图片才有referer 直接打开是没有referer
            if(/(\.png)|(\.jpg)/.test(absPath)){
                let referer = req.headers['referer'] ||  req.headers['referrer'];
                if(referer){ // 说明这个资源被引用了
                    let hostname = req.headers.host;
                    referer = url.parse(referer).host;
                    console.log(hostname,referer)
                    if(hostname !== referer && !whiteList.includes(referer)){
                        let errorFile = path.join(__dirname,'2.jpg')
                        fs.createReadStream(errorFile).pipe(res);
                        return
                    }
                }
            }
            fs.createReadStream(absPath).pipe(res);
        }else{
            res.statusCode = 404;
            res.end('Not Found');
        }
    })

}); 

server.listen(3000);
  • 什么情况referer会改变?

服务端可以自己发一个请求,自己伪造一个referer,前端改不了。

const http = require('http');
const fs = require('fs');
http.get({
    hostname:'a.zf.cn',
    port:3000,
    path:'/1.jpg',
    headers:{
        referer:'b.zf1.cn' //伪造referer
    }
},function (res) {
    res.pipe(fs.createWriteStream('test.jpg'))
});


// superagent
// Puppeteer

多语言 目前来说都是前端多语言 i18n

1.前端实现多语言vue-i18n 2.通过路径来实现多语言/zh/en 3.通过请求来实现多语音 (header)// Accept-Language: zh-CN,zh;q=0.9

const http = require('http');
const path = require('path');
const fs = require('fs');
const url = require('url');

let languages = {
    en: {
        message: {
            hello: 'hello world'
        }
    },
    ja: {
        message: {
            hello: 'こんにちは、世界'
        }
    },
    "zh-CN": {
        message: {
            hello: '你好'
        }
    }
}
const server = http.createServer((req, res) => {
    const {
        pathname
    } = url.parse(req.url, true);
    const absPath = path.join(__dirname, pathname);
    // 如果访问服务器 我需要根据header 返回不同的内容
    // Accept-Language: ja,en;q=0.8,zh;q=0.9   [{name:'ja',q:1},{name:'zh-CN1',q:1},{name:'zh-CN1',q:1}]
    let lans = req.headers['accept-language'];
    if(lans){
        let r = lans.split(',').map(lan=>{
            let [name,q] = lan.split(';');
            let obj = {}
            obj.name = name;
            if(!q){ // q是权重,默认1
                q = 'q=1';
            }
            obj.q = q.split('=')[1];
            return obj;
        }).sort((a,b)=>b.q - a.q);
        res.setHeader('Content-Type','text/plain;charset=utf-8')
        for(let i = 0; i < r.length;i++){
            let lan = languages[r[i].name];
            if(lan && lan.message){
                return res.end(lan.message.hello);
            }
        }
    }
    return res.end('not has language');
});
server.listen(3000);

断点续传 206

  • 前端需要记录一个 上传的位置 (需要维护一个上传的位置)

    举例:curl --header range:bytes=0-3 -V http://www. baidu.com

    curl命令行工具,发出网络请求,获取网络数据,看到网页源代码

    range:bytes=0-3 分段请求字节数,从0字节开始,0-3,4个字节

    206 : 如果服务器能够正常响应的话,服务器会返回 206 Partial Content 的状态码

    // range:bytes=4-7 客户端的

    // Content-Range: bytes 4-7/2381 ,2381代表文件总大小 (服务端的)

// 206 实现断点续传
// 上传 可以暂停
// 前端需要记录一个 上传的位置 (需要维护一个上传的位置)
// range:bytes=4-7 客户端的
// Content-Range: bytes 4-7/2381 文件总大小 
// curl --header range:bytes=0-3 -V http://www. baidu.com
const http = require('http');
const path = require('path');
const fs = require('fs');
const url = require('url');
const whiteList = ['a.zf.cn:3000'];
const file = fs.statSync(path.resolve(__dirname, '1.txt'));
const pathUrl = path.resolve(__dirname, '1.txt')
const size = file.size; //文件的总大小
const server = http.createServer((req, res) => {
    const {
        pathname
    } = url.parse(req.url, true);
    const range = req.headers['range']; //取浏览器的range
    if (range) {
        let matches = range.match(/(\d*)-(\d*)/);  //取range的字节范围
        let [, start = 0, end] = matches  //,全部,start默认0,end默认默认全部就是总大小size
        end = end == '' ? size : end;
        res.statusCode = 206;
        res.setHeader('Content-Range', `bytes ${start}-${end}/${size}`);
        console.log(start, end, size)
        fs.createReadStream(pathUrl, {
            start: Number(start),
            end: Number(end)
        }).pipe(res)
    } else {
        fs.createReadStream(pathUrl).pipe(res)//读整个文件,没有范围就全部
    }
});
// 实现下载 
server.listen(3000);
  • 前端分片 续传

    // 每次客户请求服务器 都返回部分内容
    
    const http = require('http');
    const fs = require('fs');
    // 续传 分片上传每次下载前端记载位置
    let start = 0;
    function download(){
        let end = start + 5
        http.get({
            host:'localhost',
            port:3000,
            method:'get',
            headers:{
                Range:`bytes=${start}-${end-1}`
            }
        },function (res) {
            // xxx-xxx/total 迅雷
            let total = res.headers['content-range'].split('/')[1]; //总大小
            res.on('data',function (data) {//监听数据到来
                fs.appendFileSync('2.txt',data);//把数据写到2.txt
                if(total > end){//判断别死循环
                    setTimeout(() => {
                        start += 5;
                        download(); //递归调用
                    }, 1000);
                }
            })
        })
    }
    download();
    

正向代理和反向代理

  • 如果代理服务器是帮助客户端的 就是正向代理vpn
  • 帮助服务器的就是反向代理nginx
  • webpack proxy 我发了一个请求 webpack
  • cdn 正向代理

正向代理 可以在请求的时候增加一些自定义属性 权限认证 ,跳板机 nginx 反向代理,作用做缓存

虚拟主机 是反向代理 ecs 服务 一个服务可以部署多个项目 多个域名 http-proxy a.zf.cn -> 网站 b.zf.cn -> 网站

80服务器 => 3000 => 4000

反向代理比较多。80服务器是反向的,做分发用的。

http-prox:npmjs.com/package/htt…

//80.js
const http = require('http');
const httpProxy = require('http-proxy');//  代理服务,分发

const proxy = httpProxy.createProxyServer();//http://npmjs.com/package/http-proxy
http.createServer((req,res)=>{
    let host = req.headers.host;
    let obj = {
        'a.zf.cn':'http://localhost:3000',
        'b.zf.cn':'http://localhost:4000',
    }
    // 增加权限判断 过滤操作
    proxy.web(req,res,{
        headers:{
            a:1
        },
        target:obj[host]
    })
}).listen(80);
// nginx 配置不同的域名 跳到不同的项目
// webpack 我们访问服务器  代理服务器会拦截用户请求 -->转发给服务器
//3000.js
const http = require('http');

http.createServer((req,res)=>{
    res.end('3000' + req.headers.a)
}).listen(3000);

//4000.js
const http = require('http');

http.createServer((req,res)=>{
    res.end('4000')
}).listen(4000);

//302.js
const http = require('http');
http.createServer((req,res)=>{
    // 判断当前用户是移动端还是pc端 
    let userAgent = req.headers["user-agent"]; // 判断用户内核
    res.statusCode = 302;
    if(userAgent.includes('iPhone')){
        res.setHeader('Location','http://www.baidu.com');
    }else {
        res.setHeader('Location','http://www.zfpx.cn');
    }
    res.end();
}).listen(3000);

header 使用场景 和 每个header的意义

HTTP常见Header

Content-Type: 数据类型(text/html等),显示方式

Content-Length: Body的长度

Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;

User-Agent: 声明用户的操作系统和浏览器版本信息;

referer: 当前页面是从哪个页面跳转过来的;

location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问,后面加上location

Cookie: 用于在客户端存储少量信息,保存以前的客户端登录信息。保存在服务器端(set-cookie),保存在客户端(cookie)

cache-control:缓存寿命

transfer-Encoding: chunked,以模块形式传输,如果存在此属性可以不写Content-Length