作为一名专业的切图工程师,我从来不care网页的header,最多关心Status Code是不是200。但是HEADER真的很重要啊,客户端从服务器端获取内容,首先就是通过HEADER进行各种沟通!HEADER可以帮助我们完成许多骚操作,提高网站的性能,用户的体验。好了让我们来feel一下。

初级骚操作

  • 多语言(Accept-Language
  • 防盗链(RefererReferered
  • gzip,简单地说就是省流量(Accept-EncodingContent-Encoding

多语言

多语言就是一个网站可以实现多种语言的切换,这里不讨论建N个网站,一个网站也个语言。这里讨论如何智能返回用户所需的语言。

server client
向server扔过去了Accept-Language
接收对方的Accept-Language
字段大概这样子zh,en-US;q=0.9,en;q=0.8
开始处理,将字段变成带权重q的数组
排序好大概长这样[{"name":"zh","q":1},{"name":"en-US","q":0.9},{"name":"en","q":0.8}]
根据权重返回拥有的语言,有zh返回zh,没有zh就返回en-US
万一我没有对方需要的语言包,怎么办?急,在线等!
没办法了,只能给对方我们的官方(默认)语言
发送,请接收
您的ACCEPT语言已匹配 这个网站挺上道的,虽然是国外网站,但知道我是中文
我们没有你所在地区的语言包 emmmm,这是火星文吗?

附赠多语言的简易实现版:

let languages = {
zh:{
title:"你好",
content:"同学"
},
en:{
title:"Hey",
content:"guy"
},
}
//设置默认语言,万一用户的语言我们不支持呢?
let defaultLanguage="zh"
let http = require('http');
function getLanguage(client_langs){
let finalLanguage=defaultLanguage
try{
if(client_langs){
//排序获取语言顺序
client_langs=client_langs.split(',').map(l=>{
let [name,q] = l.split(';');
q = q?Number(q.split('=')[1]):1
return {name,q}
}).sort((a,b)=>b.q-a.q);
//匹配服务器有的语言,并返回
for(let i = 0 ;i <languages.length;i++){
let name= languages[i].name;
if(languages[name]){
finalLanguage=name;
break;
}
}
}
}catch(e){}
return languages[finalLanguage]
}
http.createServer(function (req,res) {
//获取客户端的语言
let client_langs = req.headers['Accept-Language'];
let lan=getLanguage(client_langs)
//将语言打印到客户端
res.end(`<p>${lan.title}</p><p>${lan.content}</p>`)
}).listen(3000);

防盗链

这个技术用的最多的应该就是对于图片的限制,只有本域名可以获取到,其他域名想都不要想。

server client
在某网站上请求了一张图片
通过RefererReferered发现此网站域名不在我方白名单内
此图片不提供给某网站
此时po上了一张万用土
支持正版请上我们网站

实现原理,此处我用iframe来做例子,其实原理很简单就是对比来源,要么和请求资源一致要么在白名单内,不然就拒绝。当然如果没有来源的情况下就直接放行,万一人家是单独打开的呢,不是盗链:

let http =  require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
// 设置白名单
let whiteList = ['localhost:3000'];
http.createServer(function (req,res) {
//获取请求地址
let { pathname } = url.parse(req.url);
// 获取物理地址
let realPath = path.join(__dirname,pathname);
// 获取文件状态
fs.stat(realPath,function(err,statObj) {
if(err){
res.statusCode = 404;
res.end();
}else{
// 重点来了
let Referer = req.headers['Referer'] || req.headers['referred'];
//如果有来源
if(Referer){
//获取双方域名
let current = req.headers['host']
Referer = url.parse(Referer).host
console.log(current,Referer)
//如果域名相同活在白名单中,放行!
if (current === Referer || whiteList.includes(Referer)){
fs.createReadStream(realPath).pipe(res);
}else{
//不放行,此乃盗链!给你个眼神自行体会
fs.createReadStream(path.join(__dirname,'files/2.html')).pipe(res);
}
}else{
//没有来源,也放行。万一是单独打开的呢~
fs.createReadStream(realPath).pipe(res);
}
}
})
}).listen(3000);

gzip

现代浏览器很高级,已经可以接受压缩包了。佩服佩服。那么该如何传输压缩的网页呢?

server client
向server扔过去了Accept-Encoding
大概结构是这样的gzip, deflate, br
get到了对方的用意,开始配置压缩
如果支持压缩,先设置个头部Content-Encoding
有很多种压缩方式,按照server优先支持的匹配
在线压缩网页,成功后返回client
欢欢喜喜省了流量,而且不影响体验

附赠建议代码,大家测试的时候,别忘了创建测试的html文件

let http = require('http');
//用于压缩文件所需的库
let fs = require('fs');
let path = require('path');
//压缩的库
let zlib = require('zlib');
http.createServer(function (req,res) {
//获取客户端接受的压缩方式
let rule = req.headers['Accept-Encoding'];
// 创建原文件可读流
let originStream=fs.createReadStream(path.join(__dirname, '1.html'));
if(rule){
// 啊啊啊!正则是个坎,我怕我是跨不过去了。
if(rule.match(/\bgzip\b/)){
//如果支持压缩!一定要设置头部!
res.setHeader('Content-Encoding','gzip');
originStream=originStream.pipe(zlib.createGzip())
} else if (rule.match(/\bdeflate\b/)){
res.setHeader('Content-Encoding', 'deflate');
originStream=originStream.pipe(zlib.createDeflate())
}
}
// 输出处理后的可读流
originStream.pipe(res)
}).listen(3000);

中级操作

初级操作大多只需要靠配置HEADER即可以实现,中级我们当然要难一点,大多需要client和server打配合。

  • client给server发送内容(Content-TypeContent-Length)
  • client从server获取内容(RangeContent-Range)
  • client爬虫,抓取网页

client给server发送内容

server client
给你了一串数据,你给处理下
没头没脑,谁知道你要做什么,请设置好HEADER
好吧,告诉你Content-TypeContent-Length
可以可以,数据的内容类型是长度是很必要的
把数据传给你了,你看一下
收到~监听收到的数据是一组Buffer
接受完毕,合并Buffer
根据Content-Type对数据进行处理
格式化数据,end

Server代码

let http = require('http');
let server = http.createServer();
let arr=[]
server.on('request', (req, res)=>{
req.on('data',function (data) {
//把获取到的Buffer数据都放入熟组
arr.push(data);
});
req.on('end',function() {
// 请求结束了,好了可以开始处理断断续续收到的Buffer了
// 合并buffer
let r = Buffer.concat(arr).toString();
if (req.headers['content-type'] === 'x-www-form-urlencoded'){
let querystring = require('querystring');
r = querystring.parse(r); // a=1&b=2然后格式化
console.log("querystring",r);
} else if (req.headers['content-type'] === 'application/json'){
//听说是JSON格式的
console.log("json",JSON.parse(r));
} else{
//没有格式?那原来是啥就是啥吧。
console.log("no type",r);
}
arr=[]
res.end('结束了!');
});
})
server.listen(3000,()=>{
console.log(`server start`);
});

Client代码

// 设置请求地址的配置
let opts = {
host:'localhost',
port:3000,
path:'/',
// 头部设置很重要,头部设置很重要,头部设置很重要
headers:{
'Content-Type':'x-www-form-urlencoded',
//长度超过3就没有人理你了
"Content-Length":7
}
}
let http = require('http');
let client = http.request(opts,function (res) {
res.on('data',function (data) {
console.log(data);
})
});
client.end("a=1&b=2");

client从server获取部分内容

server client
我想要资源的部分内容
可以啊,告诉我范围
我放在HEADER中的Range了,bytes=0-3
Content-Range:bytes 0-3/7,请接受,此文件一共8字节,前3字节已经给你了 好的,那么把接下来的给我吧,bytes=4-7
给你给你都给你 end

大家都发现了吧,这样的range获取数据,完全是断点续传的简陋版啊!不过这边有一个点容易犯错就是文件大小的计算,因为文件字节的位置是按照0开始算,所以range的全范围都是0~size-1/size-1,大家注意下。

server 端

let http = require('http');
let fs = require('fs');
let path = require('path');
// 当前要下载的文件的大小
let size = fs.statSync(path.join(__dirname, 'my.txt')).size;
let server = http.createServer(function (req, res) {
let range = req.headers['range']; //获取client请求访问的部分内容
if (range) {
let [, start, end] = range.match(/(\d*)-(\d*)/);
start = start ? Number(start) : 0;
end = end ? Number(end) : size - 1; // 10个字节 size 10 (0-9)
console.log(`bytes ${start}-${end}/${size - 1}`)
res.setHeader('Content-Range', `bytes ${start}-${end}/${size - 1}`);
fs.createReadStream(path.join(__dirname, 'my.txt'), { start, end }).pipe(res);
} else {
// 会把文件的内容写给客户端
fs.createReadStream(path.join(__dirname, 'my.txt')).pipe(res);
}
});
server.listen(3000);

client端

let opts = {
host:'localhost',
port:3000,
headers:{}
}
let http = require('http');
let start = 0;
let fs = require('fs');
function download() {
//分流下载,部分下载
opts.headers.Range = `bytes=${start}-${start+3}`;
start+=4;
let client = http.request(opts,function (res) {
let total = res.headers['content-range'].split('/')[1];
res.on('data',function (data) {
fs.appendFileSync('./download.txt',data);
});
res.on('end',function () {
//结束之后,1s之后再下载
setTimeout(() => {
console.log(start,total)
if (start <= total)
download();
}, 1000);
})
});
client.end();
}
download()

client抓取网页内容,简易爬虫

这一块的操作其实很简单,只要建一个请求获取到网页就可以了。

难点在于:如何将游游有用信息剥离网页,过滤掉无用信息。

我这里抓去了百度的娱乐版,百度还算良心,是utf8的,不然就要乱码了。

let http = require('http');
let opts = {
host:'news.baidu.com',
path:'/ent'
}
//创建一个请求,获取网站内容
let client = http.request(opts,function (r) {
let arr= [];
//资源不可能一次下载完成,因此每次获取到数据都要push到arr中
r.on('data',function (data) {
arr.push(data);
});
r.on('end',function() {
//合并资源
let result = Buffer.concat(arr).toString();
//对资源进行处理,可以是变成我这样的对象,之后不管做什么处理都很方便
let content = result.match(/<ul class="ulist mix-ulist">(?:[\s\S]*?)<\/ul>/img).toString().match(/<li>(?:[\s\S]*?)<\/li>/img);
content=content.map((c)=>{
let href=/<a href="(?:[\S]*?)"/img.exec(c)
let title=/">(?:[\s\S]*?)<\/a>/img.exec(c)
return {
href:href[0].replace(/"/img,"").replace("<a href=",""),
title:title[0].replace(/">/img,"").replace("</a>","")
}
})
console.log(JSON.stringify(content))
arr= [];
})
});
client.end();

通过HTTP的HEADER完成各种骚操作的更多相关文章

  1. 闪电侠 Netty 小册里的骚操作

    前言 即使这是一本小册,但基于"不提笔不读书"的理念,仍然有必要总结一下.此小册对于那些"硬杠 Netty 源码 却不曾在千万级生产环境上使用实操"的用户非常有 ...

  2. Git科普文,Git基本原理&各种骚操作

    Git简单介绍 Git是一个分布式版本控制软件,最初由Linus Torvalds创作,于2005年以GPL发布.最初目的是为更好地管理Linux内核开发而设计. Git工作流程以及各个区域 Work ...

  3. Typescript骚操作,在TS里面直接插入HTML

    Typescript骚操作,在TS里面直接插入HTML,还有语法提示 先给大家看一个图 因为我不喜欢用很重的框架,主要是并非专业UI,但是偶尔会用到,还是觉得直接element组装受不了,想想能在ts ...

  4. awk骚操作

    一.awk自加 [root@168web3 ~]# head /data/logs/cloud_monitor_rds_cpu.log |awk '{sum+=$NF}END{print sum}' ...

  5. 如何在命令长度受限的情况下成功get到webshell(函数参数受限突破、mysql的骚操作)

    0x01 问题提出 还记得上篇文章记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门),我们讲到了一些PHP的一些如何巧妙地绕过数字和字母受限的技巧,今天我要给大家分享的是如 ...

  6. UOJ 117 欧拉回路(套圈法+欧拉回路路径输出+骚操作)

    题目链接:http://uoj.ac/problem/117 题目大意: 解题思路:先判断度数: 若G为有向图,欧拉回路的点的出度等于入度. 若G为无向图,欧拉回路的点的度数位偶数. 然后判断连通性, ...

  7. 关于map 及 map 骚操作

    关于map这个东西   很冷门..................   但是,这个博客带你稍微了解一下map:   map用法:一般当作一个下表无穷大的数组   关于它的骚操作:map的鬼畜用法,可以 ...

  8. 洛谷 P1045 麦森数 (快速幂+高精度+算位数骚操作)

    这道题太精彩了! 我一开始想直接一波暴力算,然后叫上去只有50分,50分超时 然后我改成万位制提高运算效率,还是只有50分 然后我丧心病狂开long long用10的10次方作为一位,也就是100亿进 ...

  9. Mac OS 上的一些骚操作

    本帖记录个人在使用 Mac 操作系统上的一些骚操作,不断更新,以飨读者. 快速移动网页到顶部或底部 用双指上下划触摸板吗?NO,我们有更骚的操作: command + ↑ 回到顶部 command + ...

随机推荐

  1. C#下JSON字符串的反序列化

    C#下JSON字符串的反序列化,一般都是用newtonsoft.json,比较方便..net当然也有提供相应功能,但觉得比较复杂. 所谓反序列化,就是将一个包含JSON内容的字符串,转换回指定对象(不 ...

  2. JavaScript基础 -- ECMAscript

    ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本 ...

  3. iOS开发——基础篇——assign,copy,retain之间的区别以及weak和strong的区别

    @property (nonatomic, assign) NSString *title; 什么是assign,copy,retain之间的区别? assign: 简单赋值,不更改索引计数(Refe ...

  4. 获取特定html源码 富文本编辑器 爬虫生成 dom

    python beautifulsoup获取特定html源码 - 吴悟无 - 博客园 https://www.cnblogs.com/vickey-wu/p/6843411.html PyQuery库 ...

  5. NTFS文件系统的单个文件最大到底有多大?

    于NTFS文件系统的单个文件最大到底有多大? 闲来无事突然想到这个问题,到网上搜索了一下也没有一个固定的解释. 于是到微软官方知识库去寻找答案: 注意:基础硬件限制可能会对任何文件系统施加额外的分区大 ...

  6. 有意思的RTL函数RegisterClass(在持久化中,你生成的一个新类的对象,系统并不知道他是如何来的,因此需要你注册)good

    例子1:Delphi中使用纯正的面向对象方法(这个例子最直接) Delphi的VCL技术使很多程序员能够非常快速的入门:程序员门只要简单的拖动再加上少量的几个Pascal语句,呵呵,一个可以运行得非常 ...

  7. ZOJ 1871:Steps

    Steps Time Limit: 2 Seconds      Memory Limit: 65536 KB One steps through integer points of the stra ...

  8. 类型配置命名空间 —— XML schema

    对于基于 XML 的配置,Spring 2.0 以后告别 DTD 格式(Document Type Definition)的配置文件,开始采用 Schema 格式,Schema 的突出亮点即是可让不同 ...

  9. IFRAME动态加载触发onload事件(转)

    原文地址:http://blog.ops.cc/webtech/javascript/f5nhm.html <body> <script>var iframe = docume ...

  10. POJ 1659 Frogs' Neighborhood (贪心)

    题意:中文题. 析:贪心策略,先让邻居多的选,选的时候也尽量选邻居多的. 代码如下: #pragma comment(linker, "/STACK:1024000000,102400000 ...