nodejs代理工具hoxy的使用介绍

Hoxy是一个用Node编写的完全免费、开源的Http代理软件,类似Charles和Fiddler。 它有一下功能:

  • 中断请求或者响应.
  • 查看和修改请求或响应的各个部分
  • 模拟慢速网络和延迟发送
  • 支持正向或者反向代理
  • 可以用本地文件代替远程请求
  • 请求或者响应主体支持格式: JSON, string, jQuery, buffer, 等.
  • 支持HTTP和HTTPS.

工作原理

Hoxy是客户端和服务器端之间的一个代理工具,可以在请求或者响应阶段做任何处理。配置代理后,客户端发送的请求将经历一下五个阶段

1
2
3
4
5
6
7
time ==>
-----------------------
server: 3
-------------/-\-------
hoxy: 2 4
-----------/-----\-----
client: 1 5

1.客户端发送请求.
2.Hoxy拦截并处理请求.
3.服务器端收到请求并发送响应数据.
4.Hoxy拦截响应并处理.
5.客户端收到请求.

下面通过几个例子来了解下hoxy的使用

1.打印所有请求并中断请求,发送 helloworld 给客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
"use strict";
let hoxy = require('hoxy');
// 创建一个代理服务器
let proxy = hoxy.createServer().listen(8080, function () {
console.log('start server at port:', 8080);
});
// 在请求阶段拦截并修改
proxy.intercept('request', function (req, resp, cycle) {
console.log('URL:', req.fullUrl());
resp.statusCode = 200;
resp.string = 'hello world!';
});

现在你可以直接打开浏览器直接访问http://localhost:8080,或者配置代理地址为"你的ip:8080",然后访问任意网页,即可看到"hello world!”
假如只是想查看请求内容,不做任何处理,只需要将resp相关行注释掉即可。
在请求阶段修改了响应内容之后,hoxy将不会再向服务器发送请求,直接使用修改后的内容发送给客户端。

2.修改请求或响应数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 省略前面创建代理服务器部分代码,以后示例同样
// 在请求阶段拦截并修改header
proxy.intercept({
phase: 'request',
hostname: 'test.com'
}, function (req, resp, cycle) {
console.log('URL:', req.fullUrl());
req.headers['host'] = 'test123.com';
});
// 在响应阶段拦截并修改响应数据
proxy.intercept({
phase: 'response',
fullUrl: /compconfig/, // 只修改匹配该正则的网址
as: 'json' // 将响应转化为json格式
}, function (req, resp, cycle) {
console.log('URL:', req.fullUrl());
if(resp.json.data){
resp.json.data['test'] = '123';
}
});

3.修改响应速度

1
2
3
4
5
6
7
8
9
10
// 限制上传速度为10,000字节/秒
proxy.intercept('request', function(req, resp, cycle) {
req.slow({rate:10000}); // bytes per second
});
// 模拟每个请求延迟500到1000ms
proxy.intercept('request', function(req, resp, cycle) {
req.slow({latency:randint(500, 1000)});
});

4.发送本地文件

1
2
3
4
5
6
7
8
9
// 拦截compconfig并发送本地文件
proxy.intercept({
phase: 'request',
fullUrl: /compconfig/,
}, function (req, resp, cycle) {
console.log('URL:', req.fullUrl());
resp.statusCode = 200;
return cycle.server("./mock/compconfig.json"); // 注意一定要将该对象(是个promise)返回,否则发送文件将不会生效
});

5.https代理服务器

关于https和ssl证书,可以参考

在这里我们使用openssl工具创建证书

1
2
3
4
# 创建密匙
openssl genrsa -out ./ca/my-private-root-ca.key.pem 2048
# 创建证书,我们将用这个证书为所有通过该代理的网站制作签名证书
openssl req -x509 -new -nodes -key ./ca/my-private-root-ca.key.pem -days 1024 -out ./ca/my-private-root-ca.crt.pem -subj "/C=US/ST=Utah/L=Provo/O=ACME Signing Authority Inc/CN=example.com"

创建https代理服务器

1
2
3
4
5
6
hoxy.createServer({
certAuthority: {
key: fs.readFileSync('./ca/my-private-root-ca.key.pem'),
cert: fs.readFileSync('./ca/my-private-root-ca.crt.pem')
}
}).listen(8080);

配置好代理访问https://www.baidu.com,将提示改网站证书不可信。
这里需要我们先信任刚刚制作的证书“my-private-root-ca.crt.pem”,之后就可以正常访问了。。
可以结合示例4,匹配某一个地址就发送证书给浏览器,就可以像fiddle或charles一样监控手机的https请求了。

6.使用时遇到的问题

  1. setheader失败程序退出。node4.2.6之后的版本setheader采用更严格的匹配模式比如setheader[‘host :www.baidu.com’],多个空格将不能正常执行。。
    这时需要将node降为4.2.6版本或以下(可以使用node版本管理工具nvm或者n)
  2. 出现错误时程序容易崩溃,比如链接失效等。可以使用domain或者process或者其他方式捕获异常后重启
    例如:node异常捕获domain
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const domain = require('domain');
    const fs = require('fs');
    const d = domain.create();
    d.on('error', (er) => {
    console.error('Caught error!', er);
    });
    d.run(() => {
    process.nextTick(() => {
    setTimeout(() => { // simulating some various async stuff
    fs.open('non-existent file', 'r', (er, fd) => {
    if (er) throw er;
    // proceed...
    });
    }, 100);
    });
    });

使用fis3打包单文件vue组件

在项目开发过程中,我们常把页面拆分成一个个小组件,每个组件由css、模板、js组成。
一般情况下,三个部分我们分别写在不同的文件中,但是假如页面有很多小组件,每个组件都拆分成三个文件,最终就会产生大量的小文件,不便于管理。
这时我们通过将组件的三个部门写在同一个文件来避免多个小文件,并且每个组件一个文件也更加直观。
参考webpack和browserify打包单文件组件fis3-parser-vue

详细说明

1.首先新建一个项目,文件目录如下

1
2
3
4
5
6
7
+-- index.html
+-- weight
|-- app.vue
+-- js
|-- base
|-- vue.js
|-- mod.js

文件weight/app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// weight/app.vue
<style>
h1 {
color: #f00;
}
</style>
<template>
<h1>{{msg}}</h1>
</template>
<script>
export default {
data () {
return {
msg: 'Hello world!'
}
}
}
</script>

文件index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<app></app>
<script src="js/base/mod.js"></script>
<script src="js/base/vueify-insert-css.js"></script>
<script src="js/base/vue.js"></script>
<script>
var App = require('weight/app.vue');
new Vue({
el: 'body',
components: {
app: App
}
});
</script>

2.安装打包工具

1
2
3
4
5
6
7
8
9
10
11
12
13
### 全局安装fis3
npm install fis3 -g -d
npm install fis3-hook-module -g -d
npm install fis3-hook-relative -g -d
npm install fis3-postpackager-loader -g -d
### es6相关模块(根据需要选择安装)
npm install babel-plugin-transform-runtime
npm install babel-preset-es2015
npm install fis-parser-babel2
### vue
npm install fis3-parser-vue

3.编写fis-conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// fis-conf.js
// 开启模块化包装amd,cmd
fis.hook('module', {mode: 'auto'});
// 使用相对路径。
fis.hook('relative');
fis.match('**', {relative: true});
fis
// 打包vue文件
.match(/\.vue$/i, {
rExt: '.js',
isMod: true,
isJsLike: true,
isComponent: true,
parser: fis.plugin('vue')
})
// 普通js不增加module名称
.match("*.js", {
isMod: false,
isES6: false,
isComponent: false,
useHash: false,
// 设置js文件为babel解析,支持es6的写法。
parser: fis.plugin('babel2')
})
.match('::package', {
// npm install [-g] fis3-postpackager-loader
// 分析 __RESOURCE_MAP__ 结构,来解决资源加载问题
// allInOne: true, //js&css打包成一个文件
sourceMap: true, //是否生成依赖map文件
// useInlineMap: true //是否将sourcemap作为内嵌脚本输出
postpackager: fis.plugin('loader', {})
})

4.编译

执行 fis3 release -d output,即可看到文件output/weight/app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// weight/app.js
define('weight/app.vue', function(require, exports, module) {
var __vueify_style__ = require("vueify-insert-css").insert("\nh1 {\n color: #f00;\n}\n")
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = {
data: function data() {
return {
msg: 'Hello world!'
};
}
};
if (module.exports.__esModule) module.exports = module.exports.default
;(typeof module.exports === "function"? module.exports.options: module.exports).template = "\n<h1>{{msg}}</h1>\n"
});

但是”vueify-insert-css”是什么鬼?
通过浏览器打开查看,会提示”mod.js:65 Uncaught Error: Cannot find module vueify-insert-css

5.修复

在项目中搜索 vueify-insert-css,会发现fis3-parser-vue中使用了模块vueify-insert-css,打开项目,就一个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var inserted = exports.cache = {}
exports.insert = function (css) {
if (inserted[css]) return
inserted[css] = true
var elem = document.createElement('style')
elem.setAttribute('type', 'text/css')
if ('textContent' in elem) {
elem.textContent = css
} else {
elem.styleSheet.cssText = css
}
document.getElementsByTagName('head')[0].appendChild(elem)
return elem
}

稍微改造一下就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// js/base/vueify-insert-css.js
define('vueify-insert-css', function (require, exports, module) {
var inserted = exports.cache = {}
exports.insert = function (css) {
if (inserted[css]) return
inserted[css] = true
var elem = document.createElement('style')
elem.setAttribute('type', 'text/css')
if ('textContent' in elem) {
elem.textContent = css
} else {
elem.styleSheet.cssText = css
}
document.getElementsByTagName('head')[0].appendChild(elem)
return elem
}
});

放在js/base目录,html中直接引入,重新打包,ok

6.存在问题

  • fis3-parser-vue组件对less的支持有问题,单文件中暂时无法使用less语法。sass没有试过,感兴趣的同学可以动手试试看
  • 部分编辑器可能不支持单文件vue语法高亮,使用html格式即可

npm run执行的代码和直接输入命令执行代码有什么区别?

使用npm run的方便之处在于,npm会自动把node_modules/.bin加入$PATH,这样你可以直接运行依赖程序和开发依赖程序,不用全局安装了。只要npm上的包提供命令行接口,你就可以直接使用它们,方便吧?当然,你总是可以自己写一个简单的小程序。

https

console中Command+Click即可打开连接

nodejs错误

Header name must be a valid HTTP Token
header字段不合法,降级node版本为4.2.6即可

node版代理软件hoxy的学习使用

node异常捕获domain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const domain = require('domain');
const fs = require('fs');
const d = domain.create();
d.on('error', (er) => {
console.error('Caught error!', er);
});
d.run(() => {
process.nextTick(() => {
setTimeout(() => { // simulating some various async stuff
fs.open('non-existent file', 'r', (er, fd) => {
if (er) throw er;
// proceed...
});
}, 100);
});
});

svn,cooder

1
2
svn checkout https://muzhilong@svn.baidu.com/......
upload.py -r user

Fiddle安装后上不了网

  • 关闭设置: tools -> connections -> act as system proxy on startup

charles https代理问题:破解后本地证书无法安装

node版代理软件hoxy

vim 基础

怎样在多屏幕中移动dock: 把鼠标移动到想要放置dock桌面的最下方,dock就会移动到该桌面

在做一些页面时,可能要求页面内容根据页面宽度自动调整文字大小。例如某元素p字体大小在屏幕宽度320px时为14px,其他宽度自动等比例缩放。
开始之前,需要了解一些单位

方案1: rem + 媒体选择

使用媒体选择,根据浏览器分辨率不同自动设置根节点的字体大小(间隔40px)

1
2
3
4
5
6
7
8
html { font-size: 10px; }
@media screen and (min-width:360){html{font-size: 11.25px;}} /* 360/320*10**/
@media screen and (min-width:400){html{font-size: 400/320 * 10px;}}
……
@media screen and (min-width:960){html{font-size: 960/320 * 10px;}}
/**具体元素的高度通过rem指定*/
p{font-size: 1.4rem;}

可以通过less或者css简化媒体选择部分

1
2
3
4
5
6
7
.loop(@width) when (@width > 320) {
.loop(@width - 40);
@media screen and (min-width: @width) {
html { font-size: @width/320*10px }
}
}
.loop(960);

方案2: rem + js

1
2
// 设置html的fontsize(未考虑兼容性)之后,内容元素同方案1使用rem指定大小
document.body.parentNode.style.fontSize = document.body.clientWidth/320 * 10 + 'px';

方案3: 使用vm单位

1vm代表宽度的1%,假如给元素指定fontsize的单位是vm时,页面宽度320px时,1vm代表3.2px,640px时代表6.4px,元素内容的字体大小就会自动改变;

1
p { font-size: 14/3.2vm}

方案4: 使用calc(注意:该方案不可行)

css3增加了calc属性,例如

1
div{ width: calc(100%-50px) }

那么是否可以通过calc根据页面宽度计算根节点字体大小呢?calc中模仿方案1,可得如下代码

1
html{font-size: calc(100% / 320 * 10 px)} /**错误*/

到底可以不可以呢?经测试,该方法行不动!!!至少上面的写法不可行!
为什么呢?100%页面宽度 / 320 * 10就是具体要的字体大小啊?
为什么呢?

答案: font-size的calc的100%不是代表元素宽度的,而是代表字体大小!!!!!就像font-size:100% 一样;

how to be a programmer 程序员成长之路

  • 调试技术中分治的关键和算法设计里的分治是一样的。你只要从中间开始划分,就不用划分太多次,并且能快速地调试。但问题的中点在哪里?这就是真正需要创造力和经验的地方了。
  • 百分之90的时间花在了百分之10的代码上
  • 如果你努力工作,却在你额外工作的时间里获得了很少东西,这是很愚蠢的

javascript内存泄漏查看

  • chrome profile
  • 查看时选择多个时间点的相对关系

webstorm 配置

  • 在preferences中配置全局soft wrap,在edit中配置临时
  • 配置快捷键common+wheel修改字体大小

理解正则零宽的含义

正则中所谓的零宽断言,类似于锚点字符,它们匹配指定的位置而不会匹配内容,如 ^ 匹配开头,$ 匹配结尾,\b 匹配单词边界;(?=p) 匹配「接下来的字符与 p 匹配」的位置,(?!p) 匹配「接下来的字符不与 p 匹配」的位置。\b 字符匹配单词边界,实际上就是匹配 \w 与 \W 之间的位置(\w 匹配 [a-zA-Z0-9])。很少会有人用到 \B,它匹配的是非单词边界位置,简单理解就是 \w & \w 之间位置或者 \W & \W 之间位置。

canvas使用注意

  • 中的width只能是数值,不能是百分比,符号%不起作用
  • 通过style设置宽度和高度后,canvas绘制的内容会等比例缩放

mac修改快捷键

  • 系统偏好设置➡键盘➡键盘快捷键➡应用程序快捷键,点下面的+号
  • 选择应用程序,输入菜单名称,输入快捷键,保存即可

charles 本地映射

  • 在structure中对应目录右键,选择map local,输入规则即可
  • 取消和管理: 菜单Tools-> Map local

zip压缩命令

  • zip <将要生成的压缩包名> <需要压缩的文件>
  • zip -r <将要生成的压缩包名> <需要压缩的目录路径> -x <忽略文件>

自动化工具 automator制作批量修改图片格式

css calc

  • width: (100% - 40px);
  • font-size: (100% + 2px); font-size中100%代表默认高度

修改文件权限 chmod 777 filepath

express在发送文件前修改文件,方法使用中间件修改res.write函数

1
2
3
4
5
6
7
8
9
10
11
12
app.use(function (req, res, next) {
var write = res.write;
res.write = function (chunk) {
if (~res.getHeader('Content-Type').indexOf('text/html')) {
chunk instanceof Buffer && (chunk = chunk.toString());
chunk = chunk.replace(/(<\/body>)/, "<script>alert('hi')</script>\n\n$1");
res.setHeader('Content-Length', chunk.length);
}
write.apply(this, arguments);
};
next();
});

react error

1
Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).

原因: 项目文件夹需要安装react,并且引用react时注意大小写,引入react时对大小写敏感,否则会加载多个版本的react

react dom class属性要使用className替换掉

git文件太大或者太多上传失败

  • git config –global http.postBuffer 999999999

charles使用时问题

jquery $(dom).closest(selector) 沿 DOM 树向上遍历,直到找到已应用选择器的一个匹配为止。包括当前元素

输入法回车问题

express.listen之后还能增加app.use或者app.get吗?