vesh
Version:
码农nodejs版本VESH框架,使用函数责任链模式 实现了 默认文件跳转,自定义错误页,空文件处理,URL解析,Querystring与Form参数解析,PostFiles解析,MVC自动映射,SQL自动服务化,可继承页面,静态文件,json,tjson,jsonp,tjsonp,string,void,xjson,xjosnp等等6种JSON格式,http与https等等操作
771 lines (750 loc) • 26.7 kB
JavaScript
import V from "gcl/com/coooders/common/tool";
import H from "http";
import M from "gcl/com/coooders/bean/middler";
import N from "gcl/com/coooders/db/ni";
import HS from "https";
// import mosca from 'mosca';
import socket from "ws";
import { ArrayStream } from "gcl/com/coooders/collection/arraystream";
/**
* VESH框架View层核心控件.
* 处理器基类,定义init(),onrequest(request,response,session,[next]),onresponse(request,response,session,[next]),dispose()方法分别用于初始化,处理请求,对返回值进行过滤,销毁等一系列操作,Moduler之间按照责任链方式进行串联,任何返回真,或者对最后一个参数异步调用为真的方法都视作中止责任链,沿当前路径原路返回。
*/
export const AModuler = class {
constructor(name) {
pri(this, { name: name });
}
get Name() {
return pri(this).__.name;
}
init() {
return false;
}
onrequest(req, rep, session) {
return false;
}
onresponse(req, rep, session) {
return false;
}
dispose() {
return false;
}
};
/**
* VESH框架View层核心容器类
*/
export const App = class {
constructor(config, pris = {}) {
if (!config)
throw new Error("VESH:必须输入GCL.com.coooders.io.config.config对象实例");
const that = this;
const { _, __ } = pri(
this,
V.merge(
{
config: config,
middler: new M.Middler(config),
server: null,
port: 80,
errcall: (err, rep) => {
const { _, __ } = pri(that);
console.log(err);
__.log.error("VESH:" + (err.status || "") + err.message);
//rep.setHeader('content-encoding', 'UTF-8');
rep.writeHead(500, { "Content-Type": "text/plain" });
rep.end(err.message, "UTF-8");
},
},
pris
)
);
V.merge(
__,
{
log: __.middler.getObjectByAppName(
"VESH.view",
"com.coooders.Project.VESH.Logger"
),
ni: new N.NiTemplateManager(__.middler, "Ni"),
modulers: __.middler.getObjectByAppName(
"VESH.view",
"com.coooders.Project.VESH.HttpModulers"
),
factory: __.middler.getObjectByAppName(
"VESH.view",
"com.coooders.Project.VESH.SessionDataManagerFactory"
),
},
true
);
if (__.log) {
//判断处理windows下无法监听标准错误的Bug
if (V.environment && V.environment.os != "windows")
process.stderr.on("data", (data) => __.log.error("VESH:" + data));
} else
throw new Error(
"VESH:必须定义VESH.view/com.coooders.Project.VESH.Logger对象"
);
if (!__.factory)
throw new Error(
"VESH:必须定义VESH.view/com.coooders.Project.VESH.SessionDataManagerFactory"
);
if (__.modulers && __.modulers.length > 0) {
//todo可能是异步的
__.modulers.forEach((v) => v.init());
} else
throw new Error(
"VESH:必须定义VESH.view/com.coooders.Project.VESH.HttpModulers对象"
);
}
async deal(req, rep) {
const { _, __ } = pri(this);
__.log.info("HttpServer:" + V.toJsonString(req.header) + " " + req.url);
try {
const session =
__.factory.getValue() ||
(function () {
throw new Error(
"VESH:必须定义VESH.view/com.coooders.Project.VESH.SessionDataManager对象"
);
})();
await session.init(
__.config,
__.middler,
__.log,
__.ni,
req,
rep,
__.idic
);
// console.log(123, req.url, Object.keys(__.idic).join(), this);
const data = {};
data.modulers = 0;
//module处理
await V.whileC(
() => (session.IsEnd ? null : __.modulers[data.modulers++]),
async (v) => {
try {
// console.log(74, v.Name, Object.keys(__.idic).length);
const ret = v.onrequest
? await V.callback2((call) =>
v.onrequest(req, rep, session, call)
)
: false;
if (ret) {
session.end(200);
}
} catch (err) {
__.log.error(
"VESH:" +
(v.Name ? v.Name + ": " : " ") +
(err.status || "") +
err.message
);
console.log(
"VESH:" +
(v.Name ? v.Name + ": " : " ") +
(err.status || "") +
err.message
);
session.end(err.status || 500, err.message);
}
//if (session.IsEnd) console.log('VESH-end:' + v.Name);
return false;
},
true
);
await session.update();
data.modulers--; //whileC默认会多判断一次
//console.log(data.modulers, __.modulers[data.modulers--]);
await V.whileC(
() => __.modulers[data.modulers-- - 1],
async (v) => {
try {
// console.log(91,v.Name);
return v.onresponse
? await V.callback2(v.onresponse, v, req, rep, session)
: false;
} catch (err) {
__.log.error(
"VESH:" +
(v.Name ? v.Name + ":" : "") +
(err.status || "") +
err.message
);
console.log(
"VESH onresponse:" +
(v.Name ? v.Name + ": " : " ") +
(err.status || "") +
err.message
);
session.end(err.status || 500, err.message);
data.modulers = 0;
}
return false;
},
true
);
if (await session.flush()) rep.end();
} catch (e) {
__.errcall(e, rep);
}
return false;
}
deal1(req, rep) {
rep.end("ok");
}
createServer() {
const { _, __ } = pri(this);
const server = H.createServer(function (req, rep) {
//尝试打断this传递 使用app的this进行传递
return Reflect.apply(_.deal, _, [req, rep]);
});
//传递__ 因为deal中的this是server 不是 app;
// pri(server, __);
return server;
}
start(port = 80) {
const { _, __ } = pri(this);
if (__.server) {
throw new Error("服务已经启动于" + __.port + "端口,请关闭后重新启动");
} else {
__.port = port;
// 创建服务器
__.server = _.createServer().listen(__.port);
__.server.on("timeout", () => __.log.info("HttpServer:连接超时"));
__.server.on("connection", () => __.log.info("HttpServer:发生新的连接"));
__.server.on("clientError", (err) =>
__.log.error("HttpServer:发生客户端错误" + err.stack)
);
process.on("SIGTERM", function () {
console.log("Received SIGTERM. Exiting.");
__.server.close(function () {
_.dispose();
process.exit(0);
});
});
}
}
stop() {
const { _, __ } = pri(this);
if (__.server) {
__.server.close();
__.server.removeAllListeners();
__.server = null;
}
}
dispose() {
const { _, __ } = pri(this);
__.middler.setObjectByAppName(
"VESH.view",
"com.coooders.Project.VESH.Logger",
__.log
);
__.modulers.forEach((v) => {
if (v.dispose) V.tryC(() => v.dispose());
});
}
get Config() {
return pri(this).__.config;
}
get Middler() {
return pri(this).__.middler;
}
get Ni() {
return pri(this).__.ni;
}
get Modulers() {
return pri(this).__.modulers;
}
get Factory() {
return pri(this).__.factory;
}
get Log() {
return pri(this).__.log;
}
};
/**
* Https
* @param {配置文件} config
* @param {秘钥} key
* @param {加密文件} cert
*/
export const HttpsApp = class extends App {
constructor(config, key = null, cert = null) {
super(config);
this.key = key;
this.cert = cert;
if (!(V.isValid(key) && V.isValid(cert)))
throw new Error("请输入key,cert文件作为第二第三个参数");
}
createServer() {
const { _, __ } = pri(this);
const server = HS.createServer(
{ key: this.key, cert: this.cert },
function (req, rep) {
//尝试打断this传递 使用app的this进行传递
return Reflect.apply(_.deal, _, [req, rep]);
}
);
//传递__ 因为deal中的this是server 不是 app;
// pri(server, __);
return server;
}
};
/**
* github: https://github.com/LearnBoost/socket.io 抛弃ScoketIO 因为其不是纯粹的websocket
* https://cloud.tencent.com/developer/article/1006065
* https://cloud.tencent.com/developer/article/1005550?from=15425 多项对比选择ws
* https://www.npmjs.com/package/ws#api-docs
* nginx 异步通讯服务通过 支持 ws 和 wss
* todo session.send(sessionIDs) 测试,
* @param {配置文件} config
* @param {配置参数} pris
*/
export const WsApp = class extends App {
constructor(config, pris = {}) {
pris = V.merge(
{
debug: false,
events: {},
idic: {},
queue: [], //大量队列进行数据查询
timeout: 60000,
prefix: "/ws",
callback: () => {},
},
pris
);
var events = V.merge(
{
//非open connect close error message外都是 message下的二级事件,只有二级事件才有消息ID
ping: function (data, ws) {
//真实数据是 data.data
data.ID = V.SNOWID();
data.preID = data.ID;
ws.send(V.toJsonString(data));
}, //对ping的处理自动过滤 和回复
},
pris.events || {}
);
var callback = pris.callback;
delete pris.callback;
super(
config,
V.merge(pris, {
//接收消息 {event,data:{event,ID,preID,data},error};
//发送消息 {event,ID,preID,data}
send: function (ws, data = {}) {
data.ID = data.ID || V.SNOWID();
ws.send(typeof data == "string" ? data : V.toJsonString(data));
},
checker: new (class {
constructor() {
const { _, __ } = pri(this, {
go: false,
run: function () {
if (__.go) {
setTimeout(function () {
_.check();
__.run();
}, 1000);
}
},
});
}
check() {
const { __ } = pri(this);
__.check && __.check();
}
start(check) {
const { __ } = pri(this);
__.go = true;
__.check = check;
__.run();
}
stop() {
pri(this).__.go = false;
}
})(),
})
);
const { _, __ } = pri(this, {
callback: function (ret, ws) {
try {
switch (ret.event) {
case "error":
case "open":
case "connect":
case "close":
default:
case "message":
{
//上行消息
pris.debug &&
(ret.error
? console.error(ret.error || "", ws.tsessionID)
: console.log(ret.event || "", ret.data, ws.tsessionID));
try {
ret.data =
!!ret.data && typeof ret.data == "string"
? V.json(ret.data)
: ret.data || {};
} catch (e) {}
//preload
events[ret.event] && events[ret.event](ret, ws);
//尝试进行消息处理 需要req,rep,session
events[ret.data.event] && events[ret.data.event](ret.data, ws);
//_topic和_sendkey成为服务唯一秘钥
// console.log(ret.data.data);
var url =
!!ret.data.event && ret.data.event.indexOf("/") >= 0
? ret.data.event
: pris.prefix + "/" + ret.event;
url = url + (url.indexOf(".") >= 0 ? "" : ".string");
var req = {
headers: V.merge(
{
"content-type": "ws",
protocol: "ws://",
"accept-encoding-vesh": "ws",
},
ws.headers
),
url,
method: "POST",
form: ret.data.data,
querystring: {},
message: ret,
webSocket: ws,
};
var rep = new ArrayStream();
rep.event = req.url;
rep.statusCode = 200;
rep.headers = { "content-type": "application/json" };
rep.setHeader = function (k, v) {
rep.headers[k] = v;
};
rep.getHeader = function (k, v) {
return rep.headers[k];
};
rep.writeHead = function (code, contenttype = {}) {
rep.statusCode = code;
for (var k in contenttype) {
rep.setHeader(k, contenttype[k]);
}
};
rep.end = function (sb) {
if (rep.IsEnd) return;
rep.IsEnd = true;
var data = sb || Buffer.from(rep.toArray()).toString();
//特别处理 如果没有data则不会下行
data &&
__.send(req.webSocket, {
event: rep.event,
sessionID: req.webSocket.tsessionID,
preID: req.message.data.ID,
data: data,
});
};
_.deal(req, rep);
}
break;
}
} catch (e) {
pris.debug && console.log(190, e.stack);
events["error"] && events["error"]({ event: "error", error: e }, ws);
callback({ event: "error", error: e }, ws);
} finally {
callback(ret, ws);
}
},
});
}
createServer() {
//pri 不传递 __ 的内容
const { _, __ } = pri(this, {});
var server = super.createServer();
var wss = new socket.WebSocketServer({ noServer: true });
__.checker.start(function () {
var queue = __.queue.concat();
var ret = [];
__.queue = [];
var client = null;
do {
try {
client = queue.shift();
if (
client &&
client.readyState == socket.OPEN &&
client.lastUpdate < +new Date() - __.timeout * 2
) {
if (client.tsessionID) delete __.idic[client.tsessionID];
__.debug && console.log("会话超时关闭!", client.tsessionID);
client.close();
} else ret.push(client);
} catch (e) {
console.log(e.message);
}
} while (!!client);
__.queue = __.queue.concat(ret);
// wss.clients.forEach(function(client) {
// console.log(276,client);
// //超出2倍心跳
// if (client.readyState !== socket.OPEN || client.lastUpdate<(+new Date - __.timeout*2)) {
// client.close();
// }
// });
});
// const wss = new socket.WebSocketServer({});
/**
* port: 8080,
perMessageDeflate: {
zlibDeflateOptions: {
// See zlib defaults.
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
// Other options settable:
clientNoContextTakeover: true, // Defaults to negotiated value.
serverNoContextTakeover: true, // Defaults to negotiated value.
serverMaxWindowBits: 10, // Defaults to negotiated value.
// Below options specified as default values.
concurrencyLimit: 10, // Limits zlib concurrency for perf.
threshold: 1024 // Size (in bytes) below which messages
// should not be compressed if context takeover is disabled.
}
*/
wss.on("connection", function connection(ws, req) {
//第二步
ws.on("error", function (e) {
// console.log('error',e.stack);
__.callback({ event: "error", error: e }, ws);
});
ws.on("close", function (code, reason) {
reason = Buffer.from(reason).toString("utf-8");
if (ws.tsessionID) delete __.idic[ws.tsessionID];
// console.log('close',code,reason);
__.callback({ event: "close", data: { code, reason } }, ws);
});
ws.on("message", function (message, isBinary) {
ws.lastUpdate = +new Date();
__.queue.push(ws);
//检查对象
if (isBinary) {
message = Buffer.from(message);
__.callback(
{ event: "message", data: { event: "message", data: message } },
ws
);
} else {
message = Buffer.from(message).toString("utf-8");
__.callback({ event: "message", data: message }, ws);
}
console.log("559 message", isBinary, message);
// ws.send(message);
// ws.send(JSON.stringify({type:'msg',data:'message'}));
//广播
// wss.clients.forEach(function each(client) {
// if (client.readyState === socket.OPEN) {
// client.send(data, { binary: isBinary });
// }
// });
});
__.callback({ event: "open" }, ws);
ws.lastUpdate = +new Date();
ws.headers = V.merge({}, req.headers);
ws.tsessionID = V.GUID();
__.queue.push(ws);
__.idic[ws.tsessionID] = ws;
console.log("open情况下的websocket设置", Object.keys(__.idic).join());
// console.log(220,'connection');
//生成session信息准备进行备用。生成sessionID之后准备备用
//这里的tsessionID就是标记ws备用可发送的意思。
// ws.send(JSON.stringify({type:'msg',data:'message'}));
// ...
//第一步应该先生成sessionID 进行会话保持,完全等待客户端心跳进行自动应答,保证延时时间内不响应即可关闭会话。
//生成session对象 开始按照逻辑进行deal处理
//第二步按照Socket方式进行异步通讯 参照路径方式进行 按照preload进行异步处理 其它消息进行异步处理。
});
wss.on("error", function (e) {
__.callback({ event: "error", error: e });
});
server.on("upgrade", function upgrade(request, socket, head) {
// const { pathname } = parse(request.url);
wss.handleUpgrade(request, socket, head, function done(ws) {
//第一步
wss.emit("connection", ws, request);
});
});
//传递__ 因为deal中的this是server 不是 app;
// pri(server, __);
return server;
}
};
// /**
// * Mqtt
// * https://blog.csdn.net/ziyue13/article/details/121032362
// * @param {配置文件} config
// * @param {秘钥} key
// * @param {加密文件} cert
// */
// export const MqttApp = class extends App {
// //https://www.php1.cn/detail/RS485__RS232_DeX_50f1b7d9.html
// constructor(config,sendkey="!@#$") {
// super(config,{
// port:1883,
// sendkey,
// ascoltatore:{
// type: 'mongo',
// url: '',//'mongodb://localhost:27017/mqtt',
// pubsubCollection: 'ascoltatori',
// mongo: {}
// }});
// }
// createServer(conf) {
// const { _, __ } = pri(this);
// const server = mosca.createServer(V.merge({
// port:__.port,
// ascoltatore:__.ascoltatore,
// },conf));
// //传递__ 因为deal中的this是server 不是 app;
// pri(server, __);
// return server;
// }
// start(port = 1883,dburl) {
// const { _, __ } = pri(this);
// if (__.server) {
// throw new Error('服务已经启动于' + __.port + '端口,请关闭后重新启动');
// } else {
// __.port = port;
// if(!dburl) throw new Error('请输入Mongo数据库链接地址!');
// // 创建服务器
// __.server = _.createServer({port:port,ascoltatore:{url:dburl}});
// __.server.on('timeout', () => __.log.info('MqttApp:连接超时'));
// __.server.on('clientConnected', () => __.log.info('MqttApp:发生新的连接'));
// __.server.on('ready', () => __.log.info('MqttApp:服务上线'));
// __.server.on('published', (packet,client) => {
// __.log.info('MqttApp:收到新消息',packet,client.id);
// try{
// var form = V.json(packet.payload || '{}');
// //_topic和_sendkey成为服务唯一秘钥
// if(form._topic && __.sendkey.eq(form._sendkey)){
// //这里直接转发mqtt内容
// form._topic.split(';').map(v=>{
// __.server.publish({topic:v,payload:V.toJsonString(form.data || {})});
// })
// return;
// }
// var req = {
// headers:{
// "content-type":"content-type",
// host:"127.0.0.1",port,
// protocol:'mqtt://',
// 'accept-encoding-vesh':'mqtt',
// },
// topic:packet.topic,
// url:form._url || '/',
// method:'POST',
// form:form,
// querystring:{}
// };
// var rep = new ArrayStream();
// rep.topic = packet.topic;
// rep.statusCode = 200;
// rep.headers = {"content-type":"application/json"};
// rep.setHeader = function(k,v){rep.headers[k]=v;};
// rep.end = function(sb){
// if(rep.IsEnd) return;
// rep.IsEnd = true;
// rep.topic && rep.topic.split(';').map(v=>__.server.publish({topic:v,payload:sb || Buffer.from(rep.toArray()).toString()}));
// };
// _.deal(req,rep);
// }catch(e){
// __.server.publish({topic:"client.error",payload:V.toJsonString({
// id:client.id,
// error:e.stack
// })});
// }
// return;
// });
// __.server.on('clientError', (err) => __.log.error('MqttApp:发生客户端错误' + err.stack));
// process.on('SIGTERM', function() {
// console.log("Received SIGTERM. Exiting.");
// __.server.close(function() {
// _.dispose();
// process.exit(0);
// });
// });
// }
// }
// stop() {
// const { _, __ } = pri(this);
// if (__.server) {
// __.server.close && __.server.close();
// __.server.removeAllListeners && __.server.removeAllListeners();
// __.server = null;
// }
// }
// async deal(req,rep){
// const { _, __ } = pri(this);
// __.log.info('MqttApp:' + V.toJsonString(req.header) + " " + req.url);
// try {
// const session = __.factory.getValue() || (function() { throw new Error('VESH:必须定义VESH.view/com.coooders.Project.VESH.SessionDataManager对象'); })();
// await session.init(__.config, __.middler, __.log, __.ni, req, rep);
// session.topic = req.topic;
// const data = {};
// data.modulers = 0;
// //module处理
// await V.whileC(() => session.IsEnd ? null : __.modulers[data.modulers++], async v => {
// try {
// if({
// FormModuler:1,
// FilesModuler:1
// }[v.Name]) return false;
// const ret = v.onrequest ? await V.callback2(call => v.onrequest(req, rep, session, call)) : false;
// if (ret) {
// session.end(200);
// }
// } catch (err) {
// __.log.error('VESH:' + (v.Name ? (v.Name + ': ') : ' ') + (err.status || '') + err.message);
// console.log('VESH:' + (v.Name ? (v.Name + ': ') : ' ') + (err.status || '') + err.message);
// session.end(err.status || 500, err.message);
// }
// //if (session.IsEnd) console.log('VESH-end:' + v.Name);
// return false;
// }, true);
// await session.update();
// data.modulers--; //whileC默认会多判断一次
// //console.log(data.modulers, __.modulers[data.modulers--]);
// await V.whileC(() => __.modulers[data.modulers-- - 1], async v => {
// try {
// if({
// FormModuler:1,
// FilesModuler:1
// }[v.Name]) return false;
// return v.onresponse ? await V.callback2(v.onresponse, v, req, rep, session) : false;
// } catch (err) {
// __.log.error('VESH:' + (v.Name ? (v.Name + ':') : '') + (err.status || '') + err.message);
// console.log('VESH onresponse:' + (v.Name ? (v.Name + ': ') : ' ') + (err.status || '') + err.message);
// session.end(err.status || 500, err.message);
// data.modulers = 0;
// }
// return false;
// }, true);
// rep.topic = session.topic;
// if (await session.flush())
// rep.end();
// } catch (e) {
// __.errcall(e, rep);
// }
// return false;
// }
// };
export default { AModuler, App, HttpsApp, WsApp };
const ___ = {};
const pri = V.pris();