sicarii
Version:
The zero dependency http2 nodejs multithreading framework
225 lines (165 loc) • 5.97 kB
JavaScript
const fs = require('fs'),
stream = require('stream'),
cluster = require('cluster'),
http2 = require('http2'),
cwd = process.cwd();
let server,router;
let minion;
if(!cluster.isMaster) {
minion = cluster.worker.id;
}
let config = require(process.env.config_file),
ip_config = require(cwd + config.ip_config + '.json');
config.port = process.env.PORT || config.port;
const utils = require('../utils'),
url = require('url'),
EventEmitter = require('events'),
Extend_headers = require('../extends/headers'),
path = require('path'),
qs = require('querystring'),
{ worker } = require('../utils/sync_utils');
class evt extends EventEmitter {};
let evts = {
stream: {}
}
let dest_arr = [];
server = http2.createSecureServer(Object.assign(config.server, utils.set_cert()));
//server.setSecureContext()
server.dest_arr = []
router = new evt();
server = Object.assign(server, worker);
let mth = config.stream.methods;
utils.set_methods(mth, evts, evt, router, server);
// extends
require('../extends/stream')(stream, utils, server, config);
utils.set_engine(config.template_engine, stream, config);
server.on('stream', function (stream, headers, flags) {
// ensure http2 only | fallback to request
let session = stream.session,
alpn = session.socket.alpnProtocol;
if(alpn === 'h2' || alpn === 'h2c'){
stream.headers = {};
let hmethod = headers[':method'],
hpath = headers[':path'],
ctype = headers['content-type'],
ip = utils.sort_ip(stream, headers),
socket = session.socket,
ext = path.extname(hpath).slice(1),
method = hmethod.toLowerCase(),
cpath = new URL(config.origin + hpath);
stream.ip = ip;
stream.headers['Origin'] = config.origin;
try {
if(config.stream.path_limit){
if(hpath.length > config.stream.path_limit){
return utils.err(stream, hmethod, hpath, 414, 'URI Too Long')
}
}
let in_valid = utils.stream_check(router, ip, headers);
if(in_valid){
utils.err(stream, hmethod, hpath, 401, in_valid);
return;
}
if(mth.indexOf(method) === -1){
return utils.err(stream, hmethod, hpath, 405, 'Method Not Allowed')
}
if(config.cookie_parser.enabled){
stream.cookies = utils.cookie_decode(headers['cookie'])
}
if(!config.stream.case_sensitive){
cpath.pathname = cpath.pathname.toLowerCase()
}
if(ext !== '' && method === 'get'){
return utils.serve_static(hpath, ext, stream);
} else if(config.stream.method_query.indexOf(method) !== -1){
try {
if(!utils.path_exists(server.dest_arr, method, cpath.pathname)){
return utils.err(stream, hmethod, hpath, 400, 'Bad Request');
}
let str = cpath.search.slice(1),
query = {};
if(str && str.length > config.stream.param_limit){
return utils.err(stream, hmethod, hpath, 431, 'Request Params Too Large');
}
if(config.push_handler.enabled && method === 'get'){
utils.stream_push(stream, headers, cpath);
}
if(str !== ''){
if(config.stream.querystring){
stream.qs = str;
}
str = utils.qsJSON(str);
query = str;
}
stream.query = query;
headers = new Extend_headers(headers);
evts[method].emit(cpath.pathname, stream, headers, flags);
str = query = null;
} catch (err) {
console.error(err)
}
} else if(config.stream.method_body.indexOf(method) !== -1){
let body = '';
ctype = ctype.split(';')[0];
if(!utils.path_exists(server.dest_arr, method, cpath.pathname)){
return utils.err(stream, hmethod, hpath, 500, 'Bad Request');
} else if(!ctype){
return utils.err(stream, hmethod, hpath, 415, 'Unsupported Media Type');
} else if(config.stream.content_types.indexOf(ctype) === -1){
return utils.err(stream, hmethod, hpath, 415, 'Unsupported Media Type');
}
stream.setEncoding('utf8');
stream.on('data', function(chunk, i){
body += chunk;
if(config.stream.body_limit){
if(body.length > config.stream.body_limit){
return utils.err(stream, hmethod, hpath, 413, 'Payload Too Large');
}
}
});
stream.on('end', function(){
try {
stream = utils.stream_body(stream, body, ctype)
headers = new Extend_headers(headers);
evts[method].emit(cpath.pathname, stream, headers, flags);
body = null;
} catch (err) {
return utils.err(stream, hmethod, hpath, 500, 'Bad Request', err);
}
});
stream.on('error', function(err){
return utils.err(stream, hmethod, hpath, 500, 'Bad Request', err);
});
}
if(config.logs.ip.enabled){
server.log_ip(ip, hpath)
} else if(config.logs.history.enabled){
server.log_history([Date.now(), hmethod, hpath].join('::::'))
}
} catch (err) {
return utils.err(stream, hmethod, hpath, 400, 'Bad Request', err)
}
}
})
server.push_handler = function(x){
config.push_handler.enabled = x;
return server;
}
server.pre_cache = function(){
process.send({type: 'pre_cache'});
server.pre_cache = null;
return server;
}
server.on('listening', function(err,res){
utils.cc(['worker', 'Server pid:'+ process.pid +' listening at '+ config.origin +':'+config.port],96);
})
process.on('message', function(obj){
if(obj.type === 'blacklist' || obj.type === 'whitelist'){
ip_config = utils.requireUncached(cwd + config.ip_config + '.json');
} else if(obj.type === 'log_error'){
server.log_error(obj.data)
} else if(obj.type === 'pre_cache'){
utils.pre_cache_init()
}
})
module.exports = { server, router }