jbd
Version:
jBD Framework's Core
627 lines (544 loc) • 16.2 kB
JavaScript
/**
* ==========================================
* Name: Net
* Author: Buddy-Deus
* CreTime: 2014-11-20
* Description: 网络操作
* Log
* 2015-06-09 初始化
* ==========================================
*/
jBD.define(function (module, exports, require) {
"use strict";
const net = require("net"),
http = require("http"),
url = require("url"),
qs = require("querystring"),
error = jBD.error;
let THttpClient, THttpServer;
THttpClient = (TObject => {
const METHOD = ["GET", "POST", "PUT", "DELETE"];
return TObject.extend({
className: "THttpClient",
create: function (opt) {
this.reset();
this.set(opt);
jBD.each(METHOD, d => this[d.toLowerCase()] = function (data) { return this.oper(d, data); });
},
free: function () {
this.header = null;
this.uri = null;
},
reset: function () {
this.timeout = 300;
this.header = {"Content-Type": "application/json"};
this.uri = url.parse("http://127.0.0.1");
return this;
},
set: function (tag, value) {
switch (tag) {
default:
if (arguments.length == 1) {
switch (typeof(tag)) {
case "string":
this.set("url", tag);
break;
case "number":
this.set("timeout", tag);
break;
case "object":
if (tag) {
this
.set("url", tag.uri)
.set("timeout", tag.timeout)
.set("header", tag.header);
}
break;
}
}
break;
case "url":
if (value && jBD.isString(value)) this.uri = url.parse(value);
break;
case "timeout":
if (jBD.isNumber(value) && value > 0) this.timeout = value;
break;
case "header":
if (jBD.isObject(value)) {
jBD.each(value, (d, k) => this.header[k] = d);
}
break;
}
return this;
},
oper: function (method, data) {
let dtd = jBD.Deferred(),
path = this.uri.path,
header = this.header,
req;
if (!jBD.has(method = method.toUpperCase(), METHOD)) method = METHOD[0];
if (!data) delete header["Content-Length"];
else {
switch (method) {
case "get":
if (!jBD.isSimple(data)) data = qs.stringify(data);
path += (uri.query.length > 0 ? "&" : "?") + data;
delete header["Content-Length"];
break;
case "post":
data = jBD.Conver.toString(data);
header["Content-Length"] = data.length;
break;
}
}
let httpUtil = {};
httpUtil.post = function (uri, param, opts) {
if (!opts) opts = {};
return new Promise(function (resolve, reject) {
let postData, option, protocol, req;
if (opts.hasOwnProperty("form-data") && !opts["form-data"]) postData = param;
else postData = querystring.stringify(param);
option = url.parse(uri);
option["method"] = "POST";
option["headers"] = {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(postData, "utf8")
};
if (opts.hasOwnProperty("headers")) {
for (let k in opts["headers"]) option["headers"][k] = opts["headers"][k];
}
protocol = option["protocol"] == "http:" ? http : https;
req = protocol.request(option, function (res, body) {
body = "";
res.on("data", chunk => body += chunk);
res.on("end", () => resolve(body));
res.on("error", err => reject(err));
});
req.on("error", err => reject(err));
req.write(postData);
req.end();
});
};
req = http.request(
{
method: method.toUpperCase(),
host: this.uri.hostname,
port: this.uri.port,
path: path,
timeout: this.timeout * 1000,
headers: this.header
},
res => {
let body = "";
if (res.statusCode != 200) dtd.reject(error(101, res.statusCode), res);
else {
res
.on("data", d => body += d)
.on("end", json => {
if (!jBD.isJSON(body)) dtd.resolve(body, res);
else {
json = jBD.Conver.toJSON(body);
if (json.errorCode == void(0) || !json.errorCode) dtd.resolve(json, res);
else dtd.reject(json);
}
});
}
}
);
req.on("error", e => dtd.reject(error(-999, e.message || ""), e));
if (method == "POST" && data.length > 0) req.write(data);
req.end();
return dtd.promise();
}
});
})(jBD.TObject);
THttpServer = (TObject => {
const STATE = {
inited: 0,
listening: 1,
close: 2,
error: 3
},
EVENT = ["listening", "error", "close"];
return TObject.extend({
className: "THttpServer",
create: function (opt, router = opt) {
let middle = this.__middle = {},
event = this.__event = {};
this.state = STATE.inited;
this.reset().set(opt);
this.__server = null;
jBD.each(EVENT, key => {
event[key] = [];
middle[key] = plug[key] || [];
});
this.listen = this.open;
if (this.port < 1) this.open(this.port, this.host, this.max);
},
free: function () {
},
reset: function () {
if (this.state != STATE.listening) {
this.host = "0.0.0.0";
this.port = 8080;
this.max = 0;
this.timeout = 120;
}
return this;
},
set: function (tag, value) {
if (this.state != STATE.listening) {
switch (tag) {
default:
if (arguments.length == 1) {
switch (typeof (tag)) {
case "string":
this.set("host", tag);
break;
case "number":
this.set("port", tag);
break;
case "object":
if (tag) {
this
.set("host", tag.host)
.set("port", tag.port)
.set("max", tag.max)
.set("timeout", tag.timeout);
}
break;
}
}
break;
case "host":
if (value === "localhost") this.host = "127.0.0.1";
else if (jBD.Check.IP(value)) this.host = value;
break;
case "port":
if (jBD.isNumber(value) && value > 0) this.port = value;
break;
case "max":
if (jBD.isNumber(value) && value >= 0) this.max = value;
break;
case "timeout":
if (jBD.isNumber(value) && value > 0) this.timeout = value;
break;
case "socket":
if (jBD.isFunction(value)) this.socket = value;
break;
}
}
return this;
},
open: function () {
let _this = this;
if (_this.state == STATE.listening) return _this;
let serve;
http.globalAgent.maxSockets = _this.max;
serve = _this.__server = http.createServer(function () {
});
jBD.each(EVENT, k => {
if (k == "timeout") {
serve.setTime(_this.timeout, function () {
});
}
else {
serve.on(k, function (d) {
switch (k) {
case "listening":
_this.state = true;
_this.__date = new Date();
console.info("========================================");
console.info("HTTP Listening: " + _this.host + ":" + _this.port);
console.info("****************************************\n");
break;
case "close":
_this.state = false;
d = jBD.Date.ValueOf(jBD.Date.Dec(_this.__date));
console.info("\n****************************************");
console.info("HTTP Close: " + jBD.Conver.toString(new Date(), "yyyy-mm-dd hh:nn:ss.zzz"));
console.info("HTTP Close: " + d.value + d.unit);
console.info("========================================");
break;
}
});
}
});
serve.listen(_this.port, _this.host);
return _this;
},
close: function () {
this.__server.close();
}
});
})(jBD.TObject);
let API = {};
API.httpClientIP = req => req.headers["x-real-ip"] || req.headers["x-forwarded-for"] || (req.socket && req.socket.remoteAddress) || (req.connection && req.connection.remoteAddress || req.connection.socket.remoteAddress);
/**
* http客户端请求
*
* @param {string|object} opt
* @param {string} [opt.uri]
* @param {string} [opt.method=get]
* @param {number} [opt.timeout=300]
* @param {object} [opt.header]
* @param {string} [method]
* @param {string|object} [data]
* @param {function} callback
* @returns {*}
*/
API.httpClient = function (opt, method, data = method, callback = data) {
return (new THttpClient()).set(opt).oper(method, data || "").done(callback);
};
/**
* http服务端请求
*
* @param {string|number|object} [opt] 启动参数
* @param {function} [socket] socket处理
* @returns {object}
* @return {boolean} [STATE]
*/
API.httpServer = function (opt, socket) {
return (new THttpServer()).set(opt).set("socket", socket);
};
API.tcpClient = function (host, port, timeout) {
const TYPE = ["error", "timeout", "connect", "close", "end", "data"],
fmtOpt = function (host, port, timeout) {
switch (arguments.length) {
case 1:
port = host;
timeout = null;
host = null;
break;
case 2:
port = host;
timeout = port;
host = null;
break;
}
this.HOST = jBD.isString(host) ? host : this.HOST;
this.PORT = jBD.isNumber(port) && port > 0 ? port : this.PORT;
this.TIMEOUT = jBD.isNumber(timeout) && timeout >= 0 ? timeout : this.TIMEOUT;
if (this.HOST == "localhost") this.HOST = "127.0.0.1";
if (!jBD.Check.IP(this.HOST)) this.HOST = "0.0.0.0";
return this;
},
middle = {},
api = {
TYPE: "client",
STATE: false,
HOST: "127.0.0.1",
PORT: 6140,
TIMEOUT: 0,
"set": function (type, func) {
if (arguments.length == 1) {
func = type;
type = null;
}
if (!type) type = "data";
if (jBD.has(type, TYPE)) {
switch (jBD.type(func, true)) {
case "array":
jBD.each(func, function (d) { api.set(type, d); });
break;
case "function":
if (middle[type]) middle[type].push(func);
else middle[type] = [func];
break;
}
}
return api;
},
open: function (host, port, timeout) {
fmtOpt.apply(api, arguments);
if (api.TIMEOUT > 0) {
client.setTimeout(api.TIMEOUT);
client.setKeepAlive(true, api.TIMEOUT);
}
else {
client.setTimeout(0);
client.setKeepAlive(false);
}
if (!api.STATE) client.connect(api.PORT, api.HOST);
},
close: function () { if (api.STATE) client.destroy(); },
end: function (data) { if (api.STATE) client.end.apply(client, arguments); },
send: function (data) { if (api.STATE) client.write.apply(client, arguments); }
};
let client;
switch (jBD.type(host, true)) {
case "string":
default:
fmtOpt.call(api, host, port, timeout);
break;
case "object":
client = host;
api.TYPE = "server";
api.HOST = client.remoteAddress;
api.PORT = client.remotePort;
api.STATE = true;
api.HOST = api.HOST.split(":");
api.HOST = api.HOST[api.HOST.length - 1];
if (jBD.isNumber(port)) fmtOpt.call(api, port);
break;
case "number":
fmtOpt.call(api, host);
break;
}
if (!client) client = new net.Socket();
jBD.each(TYPE, key => {
client.on(key, data => {
switch (key) {
case "connect":
api.STATE = true;
break;
case "end":
case "close":
api.STATE = false;
break;
}
jBD.each(middle[key], d => {
if (d.apply(api, [api, data]) === false) return false;
});
});
});
if (arguments.length >= 1 && api.TYPE != "server") api.open();
return api;
};
API.tcpServer = function (host, port, timeout) {
const TYPE = ["listening", "error", "close", "timeout", "connect", "data", "end"],
fmtOpt = function (host, port, timeout) {
switch (arguments.length) {
case 1:
port = host;
timeout = null;
host = null;
break;
case 2:
port = host;
timeout = port;
host = null;
break;
}
this.HOST = jBD.isString(host) ? host : this.HOST;
this.PORT = jBD.isNumber(port) && port > 0 ? port : this.PORT;
this.TIMEOUT = jBD.isNumber(timeout) && timeout >= 0 ? timeout : this.TIMEOUT;
if (this.HOST == "localhost") this.HOST = "127.0.0.1";
if (!jBD.Check.IP(this.HOST)) this.HOST = "0.0.0.0";
},
middle = {},
api = {
socket: {length: 0},
STATE: false,
HOST: "0.0.0.0",
PORT: 6140,
TIMEOUT: 0,
"set": function (type, func) {
if (arguments.length == 1) {
func = type;
type = null;
}
if (!type) type = "data";
if (jBD.has(type, TYPE)) {
switch (jBD.type(func, true)) {
case "array":
jBD.each(func, d => api.set(type, d));
break;
case "function":
if (middle[type]) middle[type].push(func);
else middle[type] = [func];
break;
}
}
return api;
},
open: function (host, port, timeout) {
fmtOpt.apply(api, arguments);
if (!api.STATE) {
if (api.HOST) server.listen(api.PORT, api.HOST);
}
},
close: function () { if (api.STATE) server.close(); },
send: function (socket, data, out) {
if (!api.STATE) return;
if (arguments.length == 1) {
data = socket;
socket = null;
}
if (!socket) socket = api.socket;
else if (api.socket.length < 1) return;
else {
if (out) {
socket = {list: api.socket, key: socket, length: 0};
for (let k in socket.list) {
if (k != "length" && k != socket.key) {
socket[k] = socket.list[k];
socket.length++;
}
}
if (socket.length < 1) return;
else {
delete socket.list;
delete socket.key;
}
}
else {
if (socket = api.socket[socket]) socket = {t: socket};
else return;
}
}
for (let k in socket) {
if (k != "length") socket[k].send(data);
}
}
},
server = net.createServer((socket, key) => {
const delSocket = (api, key) => {
return function () {
if (api.socket[key]) {
delete api.socket[key];
api.socket.length--;
}
}
};
socket = API.tcpClient(socket, api.TIMEOUT);
key = socket.HOST + ":" + socket.PORT;
socket
.set("error", middle.error)
.set("timeout", middle.timeout)
.set("end", delSocket(api, key))
.set("close", delSocket(api, key))
.set("close", middle.end)
.set("data", middle.data);
api.socket[key] = socket;
api.socket.length++;
jBD.each(middle.connect, d => {
if (d.apply(api, [socket, key]) === false) return false;
});
});
jBD.each(["listening", "error", "close"], key => {
server.on(key, data => {
if (key == "listening") {
api.STATE = true;
data = server.address();
console.info("========================================");
console.info("TCP Listening: " + data.address + ":" + data.port);
console.info("****************************************\n");
}
else api.STATE = false;
if (key == "close") api.socket = {length: 0};
jBD.each(middle[key], d => {
if (d(data) === false) return false;
});
if (key == "close") {
console.info("\n****************************************");
console.info("TCP Close: " + jBD.Conver.toString(new Date(), "yyyy-mm-dd hh:nn:ss.zzz"));
console.info("========================================");
}
});
});
if (arguments.length > 0) api.open.apply(api, arguments);
return api;
};
return API;
}, {module: module, exports: this}, ["Conver", "Check"], "Net");