latte_web2
Version:
497 lines (487 loc) • 13.9 kB
JavaScript
(function(define) { 'use strict';
define("latte_web/server/index", ["require", "exports", "module", "window"],
function(require, exports, module, window) {
var latte_lib = require("latte_lib")
, Ios = require("../io")
, Cluster = require("cluster")
, Rpc = Cluster.isMaster ? require("./rpc/master"): require("./rpc/slave")
, OneRpc = require("./rpc/one")
, Domain = require("domain")
, Http = require("http")
, Path = require("path")
, Url = require("url")
, Mines = require("../mines")
, Querystring = require("querystring")
, Post = require("../post")
, defaultConfig = {
cpus: require("os").cpus().length,
schedulingPolicy: Cluster.SCHED_RR,
port: 10096,
path: "html/",
uploadDir: "tmp/",
proxys: {
},
nginx: 0,
cache: 0,
gzip: 0
};
function Server(config) {
this.config = latte_lib.merger(defaultConfig, config);
this.gets = {};
this.posts = {};
this.getRegs = [];
this.postRegs = [];
this.befores = [];
this.afters = [];
this.rpc = new Rpc();
};
latte_lib.inherits(Server, latte_lib.events);
(function() {
var _self = this;
this.io = function(path, opts, fn) {
this.ios = this.ios || new Ios();
if(!this.config.nginx) {
this.rpc = this.rpc instanceof OneRpc ? this.rpc : new OneRpc();
}
return this.ios.add(path, opts, fn);
}
this.doMaster = function(fn) {
if(this.ios && !this.config.nginx) {
fn();
}else{
if(Cluster.isMaster) {
fn();
}
}
}
this.doSlave = function(fn) {
if(this.ios && !this.config.nginx) {
fn();
}else{
if(!Cluster.isMaster) {
fn();
}
}
}
this.onUpgrade = function(req, socket,head) {
var url = Url.parse(req.url);
var pathname = url.pathname;
var self = this;
if(self.ios.all[pathname]) {
req.gets = Querystring.parse(url.query);
self.ios.all[pathname].handleUpgrade(req, socket, head);
}
}
var getErrorString = function(err) {
return (err.stack? err.stack.toString() : err.toString());
};
this._run = function() {
var self = this
, serverDomain = Domain.create();
serverDomain.on("error", function(err) {
if(self.config.log) {
var filename = "./logs/otherError/"+latte_lib.format.dateFormat()+".log";
latte_lib.fs.writeFile(filename, getErrorString(err));
}else{
throw err;
}
});
serverDomain.run(function() {
var server = self.server = Http.createServer(function(req, res) {
var reqd = Domain.create();
reqd.add(req);reqd.add(res);
reqd.on("error", function(err) {
res.setHeader("Content-Type","text/plan");
res.end(getErrorString(err));
if(self.config.log) {
var filename = "./logs/webError/"+latte_lib.format.dateFormat()+".log";
latte_lib.fs.writeFile(filename,
req.url + "\n"
+ "gets:" + latte_lib.format.jsonFormat(req.gets) + "\n"
+ "posts:" + latte_lib.format.jsonFormat(req.posts) + "\n"
+ getErrorString(err)
);
}else{
throw err;
}
});
reqd.run(function() {
self.onRequest(req, res);
});
});
server.timeout = self.config.timeout;
server.on("upgrade", function(req, socket, head) {
self.onUpgrade(req, socket, head);
});
server.listen(self.config.port, function() {
latte_lib.debug.info(self.config.port, "start");
});
//self.rpc.Call("startHttp",[]);
});
};
this.createWorker = function() {
var worker = Cluster.fork();
this.emit("start", worker);
this.rpc.addWorker(worker);
return worker;
}
this.run = function() {
if(this.server) { return ; }
if(this.ios && !this.config.nginx) {
this._run();
}else{
if(Cluster.isMaster) {
var self = this;
self.workers = [];
for(var i = 0, len = this.config.cpus; i < len ; i++) {
var worker = self.createWorker();
}
Cluster.on("exit", function(worker, code, signal) {
var index = self.workers.indexOf(worker);
if(index != -1) {
self.workers.splice(index, 1);
}
self.rpc.removeWorker(worker);
var now = self.createWorker();
self.emit("restart", worker, now);
});
}else{
this._run();
}
}
}
this.before = function(fn) {
if(latte_lib.isFunction(fn)) {
this.befores.push(fn);
}
}
this.after = function(fn) {
if(latte_lib.isFunction) {
this.afters.push(fn);
}
}
this.use = function(opts) {
this.before(opts.before.bind(opts));
this.after(opts.after.bind(opts));
}
this.set = function(type, path) {
var self = this;
var handles = Array.prototype.slice.call(arguments, 2);
handles.forEach(function(fn) {
if(!latte_lib.isFunction(fn)) {
throw new Error(path + "," + type + "handle has not function!!");
}
});
if(this[type+"s"]) {
this[type+"s"][path] = function(req, res) {
var fns = handles.map(function(fn) {
return function(callback) {
fn(req, res, callback);
};
});
latte_lib.async.series(fns, function(err, result) {
if(err) {
res.end(err);
}
});
}
}
};
["get", "post"].forEach(function(type) {
_self[type] = function(path) {
var args = Array.prototype.slice.call(arguments);
args.unshift(type);
_self.set.apply(this, args);
}
});
this.onRequestSend = function(data, req, res) {
var fns = this.afters.map(function(fn) {
return function(callback) {
fn(req, res, callback);
};
});
latte_lib.async.parallel(fns, function(err) {
if(err) {
throw err;
}
res.end(data.toString());
})
}
this.request = function(fn, req, res) {
var self = this;
res.send = function(data) {
self.onRequestSend(data, req, res);
};
var fns = this.befores.map(function(f) {
return function(callback) {
f(req, res, callback);
};
});
latte_lib.async.parallel(fns, function(err) {
if(err) {
throw err;
}
fn( req, res);
});
};
["getReg", "postReg"].forEach(function(type) {
_self[type] = function(path) {
var args = Array.prototype.slice.call(arguments);
args.unshift(type);
_self.setReg.apply(this, args);
}
});
this.pathReg = function(path) {
var keys = [];
path = path.concat("/?")
.replace(/\/\(/g,"(?:/")
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star) {
slash = slash || "";
keys.push(key);
return ""
+ (optional? "": slash)
+ "(?:"
+ (optional? slash: "")
+ (format || "") + (capture || (format && "([^/.]+?)" || "([^/]+?)")) + ")"
+ (optional || "")
+ (star? "(/*)?": "");
})
.replace(/([\/.])/g, "\\$1")
.replace(/\*/g, "(.*)");
return {
keys : keys,
regexp: new RegExp("^" + path + "$")
};
}
this.setReg = function(type, path) {
var self = this;
var handles = Array.prototype.slice.call(arguments, 2);
handles.forEach(function(fn) {
if(!latte_lib.isFunction(fn)) {
throw new Error("setReg"+path + "," + type + "handle has not function!!");
}
});
if(this[type+"s"]) {
var object = this.pathReg(path);
object.action = function(req, res) {
var fns = handles.map(function(fn) {
return function(callback) {
fn(req, res, callback);
};
});
latte_lib.async.series(fns, function(err, result) {
if(err) {
res.end(err);
}
});
};
this[type+"s"].push(object);
}
}
this.onRequest = function(req, res) {
var self = this;
var url = Url.parse(req.url);
var pathname = url.pathname;
if(self.ios && self.ios.all[pathname]) {
req.gets = Querystring.parse(url.query);
self.ios.all[pathname].handleRequest(req, res);
return;
}
switch(req.method.toLowerCase()) {
case "post":
if(self.posts[pathname]) {
this.getPostData(req, function(err, data) {
if(err) { throw err; }
req.posts = data;
req.gets = Querystring.parse(url.query);
self.request(self.posts[pathname], req, res);
});
}else{
for(var i = 0, len = this.postRegs.length; i < len; i++) {
var regs = this.postRegs[i];
var matched = regs.regexp.exec(pathname);
if(matched) {
return this.getPostData(req, function(err, data) {
req.posts = data;
req.gets = Querystring.parse(url.query);
var keys = regs.keys;
for(var i = 0, l = keys.length; i < l; i++) {
var value = matched[i+1];
if(value) {
req.gets[keys[i]] = value;
}
}
self.request(regs.action, req, res);
});
}
}
this.proxyWeb(pathname, req, res);
}
break;
case "get":
if(self.gets[pathname]) {
req.gets = Querystring.parse(url.query);
req.posts = {};
return self.request(self.gets[pathname], req, res);
}else{
for(var i = 0, len = this.postRegs.length; i < len; i++) {
var regs = this.getRegs[i];
var matched = regs.regexp.exec(pathname)
if(matched) {
req.posts = {};
req.gets = Querystring.parse(url.query);
var keys = regs.keys;
for(var i = 0, l = keys.length; i < l; i++) {
var value = matched[i+1];
if(value) {
req.gets[keys[i]] = value;
}
}
return self.request(regs.action, req, res);
}
}
self.staticFile(pathname, req, res);
}
/*res.writeHead(404);
res.end("no the get");*/
break;
}
}
this.getPostData = function(req, callback) {
var self = this;
var post = new Post({
uploadDir: self.config.uploadDir
})
, posts = {
_files: []
};
post
.on("error", function(err) {
callback(err);
})
.on("field", function(field, value) {
posts[field] = value;
})
.on("file", function(filename, file) {
posts._files.push(file);
})
.on("text", function(data) {
posts._text = data;
})
.on("end", function() {
callback(null, posts);
});
post.parse(req);
}
this.proxyWeb = function(pathname, req, res) {
var onError = function() {
res.writeHead(404);
res.end("not find");
}
if(this.config.proxyUrl) {
var type = req.method.toLowerCase();
latte_lib.xhr[type](
this.config.proxyUrl+pathname,
req[type+"s"], {
headers: req.headers
}, function(data, headers) {
res.end();
}, function(error) {
onError()
}).on("chunk", function(data) {
res.write(data);
}).on("headers", function(headers) {
for(var i in headers) {
res.setHeader(i, headers[i]);
}
});
}else{
onError();
return;
}
}
this.findFile = function(path, callback) {
var last = path.slice(-1);
if(last == "/" || last == "\\") {
var indexs = this.config.indexs || ["index.html"];
var funs = indexs.map(function(obj) {
return function(cb) {
var p = path + obj;
latte_lib.fs.exists(p, function(exist) {
if(exist) {
return cb(p);
}
cb();
});
}
});
latte_lib.async.series(funs, function(file) {
callback(file);
});
} else {
latte_lib.fs.exists(path, function(exist) {
if(exist) {
return callback(path);
}
callback();
});
}
};
this.staticFile = function(pathname, req, res) {
var self = this;
var paths = pathname.split("/");
var proxy = this.config.proxys[paths[1]];
var mpath;
if(proxy) {
var ps = paths.splice(2);
mpath = Path.normalize(proxy + "/" + ps.join("/"));
} else{
mpath = Path.normalize(self.config.path + pathname);
}
self.findFile(mpath, function(path) {
if(!path) {
self.proxyWeb(pathname, req, res);
return
}
var fileType = Path.extname(path);
res.setHeader("Content-Type", Mines.getFileType(fileType) || "application/octet-stream");
require("fs").stat(path, function(err, stat) {
var lastModified = stat.mtime.toUTCString();
if(req.headers["If-Modified-Since"] && lastModified == req.headers["If-Modified-Since"]) {
response.statusCode = 304;
response.end();
}else{
res.setHeader("Last-Modified", lastModified);
if(self.config.cache) {
var expires = new Date();
var maxAge = self.config.cache || 0;
expires.setTime(expires.getTime() + maxAge* 1000);
res.setHeader("Expires", expires.toUTCString());
res.setHeader("Cache-Control", "max-age=" + maxAge);
}
var stream = require("fs").createReadStream(path, {
flag:"r",
autoClose: true
});
var Zlib = require("zlib");
if(self.config.gzip &&
stat.size > self.config.gzip ) {
var acceptEncoding = req.headers['accept-encoding']
if(acceptEncoding.match(/\bgzip\b/)) {
res.setHeader("Content-Encoding", "gzip");
stream.pipe(Zlib.createGzip().pipe(res));
}else if(acceptEncoding.match(/\bdeflate\b/)) {
res.setHeader("Content-Encoding", "deflate");
stream.pipe(Zlib.createDeflate()).pipe(res);
}
}else{
stream.pipe(res);
}
}
});
});
}
}).call(Server.prototype);
module.exports = Server;
});
})(typeof define === "function"? define: function(name, reqs, factory) { factory(require, exports, module); });