Lightweight WSGI framework, inspired by ROR & Laravel frameworks
Though there're many frameworks have done WSGI server features, however they are somehow a little heavy;
Thus I make this project, and make it simple & almost just a pure nodejs http server.
- Built-in
- Execution Timeout Customizing for each route
- Exception-Handling(Exceptions in Middleware & Route handlers will be caught)
- CSRF Checking
- Path parameters parsing, routing path & methods matching
- Query parameters(built-in module url)
- Form fields parsing
- FastCGI style multiple processes serving(depending on CPU cores by default), reducing response time
- (built-in module cluster)
- Optional extension
npm i wsgilite# Get this repo ready
git clone https://github.com/TeaEntityLab/wsgiLite.js.git
cd wsgiLite.js/demo
npm install
# The demo server will listen on port 3333
npm run serve
# WebSocket demo(tests by `wscat` cli tools)
npm run serve-websocket
npm install -g wscat
wscat -c "ws://localhost:3333/wss1"
wscat -c "ws://localhost:3333/wss2"In this example, we combined wsgilite server and WebSocket server(built by ws(npmgithub))
WSGILite & WebSocket work perfectly together(WSGILite acts just like a native built-in http(s) server)
const{
WSGILite,}=require('wsgilite/wsgilite');const{
Template,}=require('wsgilite/template');const{
mimeMap,
defMiddlewareServeFileStatic,
defMiddlewareServeFileStaticWithDirList,}=require('wsgilite/file');const{
defFormCsrfCheckRoutes,
defHeaderCsrfCheckRoutes,
getCSRF_token,
generateCSRFFormInput,}=require('wsgilite/csrf');// Settingsconstwsgilite=newWSGILite({secret: 'abcdefg',// The secret key for CSRFlogProcessMessage: true,// Log the multiple processes starting/ending messages.debug: true,// Debug mode, it will show stacktrace of errors in the responsesisHttps: false,// Is it a https server?processNum: require('os').cpus().length,// N + 1 processes (cluster: 1 * master + n * fastcgi style http/https serving)// processNum: 0, // Single process// createServerOptions: {}, // Additional createServer options for http/https.createServer(options)onServerCreated: function(httpServer){httpServer.maxHeadersCount=0;console.log('server.maxHeadersCount set');},// Callback on one of servers created(this will be called in Multiple processes)onMessageMaster: function(worker,msg,handle){console.log(`master got message: ${msg.event}`);},// Optional: We could monitor and handle communications between cluster master & workersonMessageWorker: function(msg,handle){console.log(`worker got message: ${msg.event}`);},// Optional: We could monitor and handle communications between cluster master & workersworkerServeTimesToRestart: 500,// Each child worker will auto-restart after it served 500 requests});constredirect=wsgilite.redirectAsFunction();// Define redirect() in simple & shorter function style.consttemplate=newTemplate({baseDir: "demo/template",// Define the base dir of template files});constrender=template.renderAsFunction();// Define render() in simple & shorter function style.// Middlewareswsgilite.addMiddleware(async(request,response,meta)=>{meta.msg3='I got it3';});wsgilite.addMiddleware(function*(request,response,meta){returnPromise.resolve(0).then(()=>meta.msg2='I got it2');});wsgilite.addMiddleware((request,response,meta)=>{meta.msg='I got it';});// CSRF routes definingwsgilite.addMiddleware(defFormCsrfCheckRoutes(['/upload',// Your can define more paths here],wsgilite));wsgilite.addMiddleware(defHeaderCsrfCheckRoutes(['/upload2',// Your can define more paths here],wsgilite));// Routes// Print the `meta` objectwsgilite.GET('/',(request,response,meta)=>{returnJSON.stringify(meta);// {"_skip404":true,"_url_path":"/","msg3":"I got it3","msg2":"I got it2","msg":"I got it"}});// The server will terminatewsgilite.GET('/terminate',(request,response,meta)=>{response.end("terminate");wsgilite.terminate();});// Path parameters(until `/` or `?` or the end)wsgilite.GET('/user/:id',function*(request,response,meta){returnyieldPromise.resolve(meta);// {"_skip404":true,"_url_path":"/user/theID","msg3":"I got it3","msg2":"I got it2","msg":"I got it","id":"theID"}});// Path parameters(until `?` or the end)wsgilite.GET('/file*relativePath',(request,response,meta)=>{defMiddlewareServeFileStatic('demo')(request,response,meta);});// Show the CSRF usages (token only & a form hidden input with the token)wsgilite.GET('/csrf',function*(request,response,meta){returngetCSRF_token(request,response)+` ${generateCSRFFormInput(request,response)}`;// CSRF_token});// CSRF checking (form)wsgilite.POST('/upload',(request,response,meta)=>{return'CSRF_token ok';// CSRF_token ok});// CSRF checking (header)wsgilite.POST('/upload2',(request,response,meta)=>{return'x-csrf-token ok';// x-csrf-token ok});wsgilite.GET('/heartbeat',async(request,response,meta)=>{return"ok";// ok});// Redirect to an existing routewsgilite.GET('/heartbeat2',async(request,response,meta)=>{redirect('/heartbeat')(request,response,meta);// ok});// Redirect to a non-existing route(will 404)wsgilite.GET('/heartbeat3',async(request,response,meta)=>{redirect('/heartbeat999')(request,response,meta);// 404 File not found.});// Nested routing definitionswsgilite.defSubRoute('test',function(defSub){defSub.defSubRoute('change',function(defSub){defSub.defSubRoute('for',function(defSub){defSub.GET('it',async(request,response,meta)=>{return"Here we go";// Here we go})});});});// Template usageswsgilite.GET('/template',async(request,response,meta)=>{returnrender("features",{"title": "JavaScript Templates","url": "https://github.com/blueimp/JavaScript-Templates","features": ["lightweight & fast","powerful","zero dependencies"]}).catch((e)=>{response.statusCode=500;returne.stack;});// ok});// Post jobs to cluster master(if wsgilite.config.processNum > 0)wsgilite.addClusterMasterRequestHandler(async(worker,msg,handle)=>{// throw new Error("There's an exception");if(msg&&msg.data&&msg.data.action==='readrecord'){return{data: 'user01'};}});wsgilite.GET('/requestActionOnClusterMaster',asyncfunction(request,response,meta){// {"event":"MSG_WSGILITE_DO_THINGS_WORKER_SUCCESS","result":[{"data":"user01"}]}returnwsgilite.requestActionOnClusterMaster({action: 'readrecord'}).catch((e)=>{console.log(`I got error: ${e.errorMessage}`);returne.errorMessage;});});// Post jobs to cluster master(Timeout cases)wsgilite.addClusterMasterRequestHandler(async(worker,msg,handle)=>{if(msg&&msg.data&&msg.data.action==='streaming_timeout'){constsnooze=require('fpEs/fp').snooze;letstartTimestamp=Date.now();while(Date.now()-startTimestamp<5*1000){awaitsnooze(500);if(msg.cancel){thrownewError('cluster master 5secs job: cancelled');}}console.log(msg);return'ok';}});wsgilite.GET('/requestActionOnClusterMasterTimeout',asyncfunction(request,response,meta){// Timeout for 3000 msreturnwsgilite.requestActionOnClusterMaster({action: 'streaming_timeout'},3000).catch((e)=>{console.log(`I got error: ${e.errorStacktrace}`);returne.errorStacktrace;});});// if (require('cluster').isMaster) {// // It could be called on cluster master// wsgilite.requestActionOnClusterMaster({action: 'readrecord'}).catch((e)=>{// return e.errorMessage;// }).then((msg)=>console.log(msg));// }// Exceptionwsgilite.GET('/exception',asyncfunction(request,response,meta){thrownewError("There's an exception");// Error: There's an exception});// TimeoutletrouteTimeout=wsgilite.GET('/timeout',asyncfunction(request,response,meta){varfetch=require('node-fetch');awaitfetch('https://www.sample-videos.com/video/mp4/240/big_buck_bunny_240p_30mb.mp4');return"ok";});routeTimeout.timeout=5000;wsgilite.listen(3333,'localhost',function(){console.log('Server up');});