gcl
Version:
码农村nodejs类库,完成解决回调陷阱,配置框架,IOC框架,统一多DB访问框架,可扩展日志框架和io/db/net/thread/pool 克隆表达式,加解密算法等等一系列基础类库和算法的实现
653 lines (633 loc) • 19.3 kB
JavaScript
import V from "../common/tool";
import H from "http";
import HS from "https";
import U from "url";
import Z from "zlib";
import F from "fs";
import P from "path";
import { ArrayStream } from "../collection/arraystream";
import C from "./cookie";
import I from "../io/tool";
import { stringify } from "querystring";
import { decode } from "iconv-lite";
/**
* 处理字符集不标准问题
* @param {*} buffer
* @param {*} encoding
*/
const convert = (buffer = new Buffer(), coding = "utf-8") =>
!coding.eq("utf-8") && !coding.eq("utf8")
? decode(buffer, coding)
: buffer.toString();
/**
* request全Tool对象的基本方法可以输入 options=url.parse,stream,func(err,resp,code,cencoding,encoding); 返回req 需要手动完成write与end操作 可以针对GZIP等方式自动解压还原文件内容
*/
export const request = (options, stream, func) => {
options = V.merge(
{
method: "GET",
headers: {
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.11 Safari/537.36",
"Accept-Encoding": "gzip, deflate",
},
},
options,
);
const HA = options.protocol.toLowerCase() == "https:" ? HS : H;
return HA.request(options, (resp) => {
var contentencoding =
(resp.headers["content-type"] &&
resp.headers["content-type"].split("=")[1]) ||
"UTF-8";
var encoding = resp.headers["content-encoding"] || "";
resp.on("error", (err) => {
console.log(err);
if (func) func.apply(null, [err]);
});
stream.on("finish", function () {
if (resp.statusCode <= 400) {
if (func)
func.apply(null, [
null,
resp,
resp.statusCode,
contentencoding,
encoding,
]);
} else {
//V.showException(new Error(resp.statusCode + '状态不等于200'));
if (func)
func.apply(null, [
new Error(
resp.statusCode +
"状态不等于正常值:" +
(stream.toArray
? convert(
Buffer.from(stream.toArray()),
contentencoding || encoding,
)
: ""),
),
]);
}
});
switch (encoding.toLowerCase()) {
case "gzip":
resp.pipe(Z.createGunzip()).pipe(stream);
break;
case "deflate":
resp.pipe(Z.createInflate()).pipe(stream);
break;
default:
resp.pipe(stream);
break;
}
});
};
export const WebClient = class {
constructor() {
const { _, __ } = pri(this, { cookies: {}, timeout: 60000 });
// _.cookies = {};
// _.timeout = 60000;
}
get cookies() {
return pri(this).__.cookies;
}
get timeout() {
return pri(this).__.timeout;
}
set timeout(v) {
return (pri(this).__.timeout = isNaN(+v) ? 60000 : +v);
}
get(url, data = "", headers = {}, method = {}, original = false) {
const { _, __ } = pri(this);
data = typeof data == "string" ? data : stringify(data);
return V.callback2((call) => {
const stream = new ArrayStream();
url = data
? `${url}${url.indexOf("?") >= 0 ? "&" : "?"}${("" + data).trim("&")}`
: url;
const req = request(
V.merge(
{
headers: { Cookie: C.toString(_.cookies, url) },
},
U.parse(url),
{ headers: headers },
method,
),
stream,
(err, resp, code, cencoding, encoding) => {
if (err) call(err);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(
null,
original
? Buffer.from(stream.toArray())
: convert(Buffer.from(stream.toArray()), cencoding || encoding),
);
}
},
);
req.on("error", function (e) {
call(e);
});
req.setTimeout(_.timeout);
req.end();
});
}
post(url, data = "", headers = {}, method = {}) {
const { _, __ } = pri(this);
//兼容JSON格式输入
data = typeof data == "string" ? data : stringify(data);
return V.callback2((call) => {
const stream = new ArrayStream();
const req = request(
V.merge(
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(data),
Cookie: C.toString(_.cookies, url),
},
},
U.parse(url),
{ headers: headers },
method,
),
stream,
(err, resp, code, cencoding, encoding) => {
if (err) call(err);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(
null,
convert(Buffer.from(stream.toArray()), cencoding || encoding),
);
}
},
);
req.on("error", function (e) {
call(e);
});
req.write(data);
req.setTimeout(_.timeout);
req.end();
});
}
getJson(url, data = "", headers = {}, method = {}) {
const { _, __ } = pri(this);
data = typeof data == "string" ? data : stringify(data);
return V.callback2((call) => {
const stream = new ArrayStream();
url = data
? `${url}${url.indexOf("?") >= 0 ? "&" : "?"}${("" + data).trim("&")}`
: url;
const req = request(
V.merge(
{
method: "GET",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(data),
Cookie: C.toString(_.cookies, url),
},
},
U.parse(url),
{ headers: headers },
method,
),
stream,
(err, resp, code, cencoding, encoding) => {
if (err) call(err);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(
null,
convert(Buffer.from(stream.toArray()), cencoding || encoding),
);
}
},
);
req.on("error", function (e) {
call(e);
});
// req.write(V.toJsonString(data));
req.setTimeout(_.timeout);
req.end();
});
}
/**
* post方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
postJson(url, data = {}, headers = {}, method = {}) {
const { _, __ } = pri(this);
return _.post(
url,
V.toJsonString(data),
V.merge(
{
"Content-Type": "application/json",
},
headers,
),
method,
);
}
/**
* post方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async upload(url, path, headers = {}, method = {}) {
const { _, __ } = pri(this);
let paths = V.isArray(path)
? path
: path.split(";").filter((v) => V.isValid(v));
let err = V.sb();
let ret = [];
let boundaryKey = Math.random().toString(16);
let enddata = `\r\n----${boundaryKey}--`; //定义尾部
var i = 0;
var contentlength = 0;
await V.each(
paths,
async (v) => {
const pjson = P.parse(P.resolve(P.normalize(v)));
v = P.format(pjson);
let isTrue = await V.callback2((call2) =>
F.exists(v, (ist) => call2(null, ist)),
);
if (!isTrue) {
err.append(v + "目标文件未找到");
return false;
}
try {
let stat = await V.callback2(F.stat, F, v);
var content = Buffer.from(
"\r\n----" +
boundaryKey +
"\r\n" +
"Content-Type: application/octet-stream\r\n" +
'Content-Disposition: form-data; name="' +
"file" +
i++ +
'"; filename="' +
pjson.base +
'"\r\n' +
"Content-Transfer-Encoding: binary\r\n\r\n",
"utf-8",
);
ret.push({ content: content, path: v });
contentlength += content.length + stat.size;
} catch (e) {
err.append(v + "目标文件stat获取出现问题:" + e.message);
}
return false;
},
true,
);
return V.callback2((call) => {
const stream = new ArrayStream();
const req = request(
V.merge(
{
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=--" + boundaryKey,
"Content-Length": contentlength + Buffer.byteLength(enddata),
Cookie: C.toString(_.cookies, url),
},
},
U.parse(url),
{ headers: headers },
method,
),
stream,
(err2, resp, code, cencoding, encoding) => {
if (err2) call(err.clear() + "\r\n" + err2.stack);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(
null,
convert(Buffer.from(stream.toArray()), cencoding || encoding),
);
}
},
);
req.on("error", function (e) {
call(e);
});
req.setTimeout(_.timeout);
V.each(
ret,
(v, call2) => {
var file = F.createReadStream(v.path, { bufferSize: 4 * 1024 });
//写入头部
req.write(v.content);
file.on("end", call2);
//写入文件
file.pipe(req, { end: false });
},
true,
)
.then(() => req.end(enddata))
.catch((e) => console.log(e.stack));
});
}
/**
* post方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async form(url, data, headers = {}, method = {}) {
const { _, __ } = pri(this);
let err = V.sb();
let boundaryKey = Math.random().toString(16);
let enddata = `\r\n----${boundaryKey}--`; //定义尾部
var contentlength = 0;
console.log(
Object.keys(data)
.map((k) => {
return (
"\r\n----" +
boundaryKey +
"\r\n" +
'Content-Disposition: form-data; name="' +
k +
'"\r\n\r\n' +
data[k]
);
})
.join("") + "\r\n",
);
var content = Buffer.from(
Object.keys(data)
.map((k) => {
return (
"\r\n----" +
boundaryKey +
"\r\n" +
'Content-Disposition: form-data; name="' +
k +
'"\r\n\r\n' +
data[k]
);
})
.join("") + "",
"utf-8",
);
contentlength += content.length;
return V.callback2((call) => {
const stream = new ArrayStream();
const req = request(
V.merge(
{
method: "POST",
headers: {
"Content-Type": "multipart/form-data; boundary=--" + boundaryKey,
"Content-Length": contentlength + Buffer.byteLength(enddata),
Cookie: C.toString(_.cookies, url),
},
},
U.parse(url),
{ headers: headers },
method,
),
stream,
(err2, resp, code, cencoding, encoding) => {
if (err2) call(err.clear() + "\r\n" + err2.stack);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(
null,
convert(Buffer.from(stream.toArray()), cencoding || encoding),
);
}
},
);
req.on("error", function (e) {
call(e);
});
req.setTimeout(_.timeout);
req.write(content);
req.end(enddata);
});
}
/**
* get方法实现保留cookie的会话下载文件操作url,path,func(err,data)
*/
async download(url, path, headers = {}, method = {}) {
const { _, __ } = pri(this);
await I.createDir(path);
let file = F.createWriteStream(path);
let opt = U.parse(url);
opt.headers = V.merge({ Cookie: C.toString(_.cookies, url) }, headers);
V.merge(opt, method, true);
return V.callback2((call) => {
let req = request(opt, file, (err, resp, code, cencoding, encoding) => {
if (err) call(err);
else {
C.merge(_.cookies, C.parse(resp.headers["set-cookie"]));
call(null, true);
}
});
req.on("error", function (e) {
call(e);
});
req.setTimeout(_.timeout);
req.end();
});
}
/**
* put方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async put(url, data = "", headers = {}, method = {}) {
const { _, __ } = pri(this);
return _.post(
url,
typeof data == "String" ? data : V.toJsonString(data),
headers,
V.merge(method, { method: "PUT" }),
);
}
/**
* host方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async host(url, data = "", headers = {}, method = {}) {
return this.put(url, data, headers, V.merge(method, { method: "HOST" }));
}
/**
* delete方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async del(url, data = "", headers = {}, method = {}) {
return this.put(url, data, headers, V.merge(method, { method: "DELETE" }));
}
/**
* patch方法实现保留cookie的会话上传文件操作url,path(;分隔的字符串或者文件名数组),func(err,data)
* 鸣谢http://www.soso.io/article/48643.html
*/
async patch(url, data = "", headers = {}, method = {}) {
return this.put(url, data, headers, V.merge(method, { method: "PATCH" }));
}
};
/**
* 新建WebClient类对象返回get方法的对象url,func(err,data)
*/
export const get = (url, data, headers, method, original) =>
new WebClient().get(url, data, headers, method, original);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const post = (url, data, headers, method) =>
new WebClient().post(url, data, headers, method);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const host = (url, data, headers, method) =>
new WebClient().host(url, data, headers, method);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const put = (url, data, headers, method) =>
new WebClient().put(url, data, headers, method);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const patch = (url, data, headers, method) =>
new WebClient().patch(url, data, headers, method);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const del = (url, data, headers, method) =>
new WebClient().del(url, data, headers, method);
/**
* post(url,func):新建WebClient类对象返回post方法的对象url,string || Buffer,func(err,data)
*/
export const form = (url, data, headers, method) =>
new WebClient().form(url, data, headers, method);
/**
* 新建WebClient类对象返回post方法的对象url,string || JSON,func(err,data)
*/
export const postJson = (url, data, headers, method) =>
new WebClient().postJson(url, data, headers, method);
/**
* 新建WebClient类对象返回post方法的对象url,string || JSON,func(err,data)
*/
export const getJson = (url, data, headers, method) =>
new WebClient().getJson(url, data, headers, method);
/**
* 新建WebClient类对象返回post方法的文件对象url,path,func(err,data)
*/
export const upload = (url, path, headers, method) =>
new WebClient().upload(url, path, headers, method);
/**
* 新建WebClient类对象返回get方法的文件对象url,path,func(err,data)
*/
export const download = (url, path, headers, method) =>
new WebClient().download(url, path, headers, method);
/**
* createReverseProxy(port,[address]):非常简单的TCP代理服务器
*/
export const createReverseProxy = function (port, address) {
if (address) {
} else
throw new Error(
"Need address Array(e.g [{host:127.0.0.1,port:1001},{host:127.0.0.1,port:1002}]) as ports",
);
address = V.isArray(address) ? address : [address];
var w = 0;
var proxy = (client, c) => {
client.pipe(c);
c.pipe(client);
};
var find = () => {
var start = w;
w = (w + 1) % address.length;
while (start != w) {
var p = address[w];
if (p.err) {
if (p.err.passtime.sub("s", new Date()) < 0) {
delete p.err;
return p;
}
} else return p;
w = (w + 1) % address.length;
}
return address[start];
};
var tcpServer = net
.createServer((c) => {
var p = find();
try {
var client = net.createConnection(p.port, p.host, () =>
proxy(client, c),
);
} catch (e) {
console.log(e.stack);
p.err = { e: e, passtime: new Date().add("s", 5) };
c.end(new Buffer(e.message, "utf-8"));
}
})
.listen(port, function () {
console.log("Proxy Server listen " + port + " Now!");
});
tcpServer.on("error", function (err) {
output("server error ,check the port..." + err);
});
return tcpServer;
};
/**
* createReverseProxy(proxy,host,proxyports):非常简单的TCP代理服务器
*/
export const createReverseProxy2 = function (port, host, proxyports) {
if (proxyports) {
} else
throw new Error("Need ProxyPorts Array(e.g [1001,1002,1003]) as ports");
proxyports = V.isArray(proxyports) ? proxyports : [proxyports];
var w = 0;
var proxy = (client, c) => {
client.pipe(c);
c.pipe(client);
};
var tcpServer = net
.createServer((c) => {
w = (w + 1) % proxyports.length;
var p = proxyports[w];
var client = host
? net.createConnection(p, host, () => proxy(client, c))
: net.createConnection(p, () => proxy(client, c));
})
.listen(port, function () {
console.log("Proxy Server listen " + port + " Now!");
});
tcpServer.on("error", function (err) {
output("server error ,check the port..." + err);
});
return tcpServer;
};
export default {
request,
WebClient,
get,
getJson,
post,
postJson,
upload,
download,
createReverseProxy,
createReverseProxy2,
form,
host,
patch,
put,
del,
};
const pri = V.pris();