spritz 0.5.17 → 0.5.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/spritz.js +613 -615
package/spritz.js CHANGED
@@ -1,78 +1,76 @@
1
1
  "use strict";
2
2
 
3
3
  /*
4
- Spritz Web server framework
5
- Version: 0.5.2
6
- Author: David Oliveira <d.oliveira@prozone.org>
4
+ Spritz Web framework
7
5
  */
8
6
 
9
7
 
10
8
  var
11
- http = require('http'),
12
- https = require('https'),
13
- cluster = require('cluster'),
14
- qs = require('querystring'),
15
- formidable = require('formidable');
9
+ http = require('http'),
10
+ https = require('https'),
11
+ cluster = require('cluster'),
12
+ qs = require('querystring'),
13
+ formidable = require('formidable');
16
14
 
17
15
 
18
16
  // Our data (from the default/first server)
19
- exports.routes = {};
20
- exports.rxRoutes = [];
21
- exports.statusRoutes = {};
22
- exports.reqSeq = 0;
23
- exports.hooks = {
24
- 'setroute': [], // sync |
25
- 'arrive': [], // async | done
26
- 'readheaders': [], // async | done
27
- 'read': [], // async | done
28
- 'findroute': [], // async | done
29
-
30
- 'beforewritehead': [], // async | done
31
- // writehead -> // async | done
32
- 'beforewritedata': [], // async | done
33
- // writedata -> // async | done
34
- 'beforefinish': [], // async | done
35
- 'finish': [] // async | done
17
+ exports.routes = {};
18
+ exports.rxRoutes = [];
19
+ exports.statusRoutes = {};
20
+ exports.reqSeq = 0;
21
+ exports.hooks = {
22
+ 'setroute': [], // sync |
23
+ 'arrive': [], // async | done
24
+ 'readheaders': [], // async | done
25
+ 'read': [], // async | done
26
+ 'findroute': [], // async | done
27
+
28
+ 'beforewritehead': [], // async | done
29
+ // writehead -> // async | done
30
+ 'beforewritedata': [], // async | done
31
+ // writedata -> // async | done
32
+ 'beforefinish': [], // async | done
33
+ 'finish': [] // async | done
36
34
  };
37
- exports.globalHooks = {};
35
+ exports.globalHooks = {};
38
36
 
39
37
 
40
38
  // Create a new server instance
41
39
  exports.newServer = function(){
42
40
 
43
- var
44
- self = this,
45
- newServer
41
+ var
42
+ self = this,
43
+ newServer
46
44
 
47
- // Clone the current object
48
- newServer = self.cloneServer();
45
+ // Clone the current object
46
+ newServer = self.cloneServer();
49
47
 
50
- // Reset some data
51
- newServer.routes = {};
52
- newServer.rxRoutes = [];
53
- newServer.statusRoutes = {};
54
- newServer.reqSeq = 0;
48
+ // Reset some data
49
+ newServer.routes = {};
50
+ newServer.rxRoutes = [];
51
+ newServer.statusRoutes = {};
52
+ newServer.reqSeq = 0;
55
53
 
56
- // Cleanup hooks (copy them from globalHooks or initialize them)
57
- for ( var h in newServer.hooks )
58
- newServer.hooks[h] = exports.globalHooks[h] ? exports.globalHooks[h].slice(0) : [];
54
+ // Cleanup hooks (copy them from globalHooks or initialize them)
55
+ for ( var h in newServer.hooks )
56
+ newServer.hooks[h] = exports.globalHooks[h] ? exports.globalHooks[h].slice(0) : [];
59
57
 
60
- // Delete newServer()
61
- delete newServer.newServer;
58
+ // Delete newServer()
59
+ delete newServer.newServer;
62
60
 
63
- // Return it
64
- return newServer;
61
+ // Return it
62
+ return newServer;
65
63
 
66
64
  };
67
65
 
68
66
  // Clone a currently existing server instance
69
67
  exports.cloneServer = function(){
70
68
 
71
- var
72
- self = this;
69
+ var
70
+ self = this;
73
71
 
74
- // Clone the current object and return it
75
- return _merge(self,{});
72
+ // Clone the current object and return it
73
+ return _merge(self,{});
76
74
 
77
75
  };
78
76
 
@@ -80,15 +78,15 @@ exports.cloneServer = function(){
80
78
  // Use a certain (or a list of) module(s)
81
79
  exports.use = function(modules,args) {
82
80
 
83
- var
84
- self = this,
85
- _mods = (modules instanceof Array) ? modules : [modules];
81
+ var
82
+ self = this,
83
+ _mods = (modules instanceof Array) ? modules : [modules];
86
84
 
87
- // Initialize all the modules
88
- _mods.forEach(function(mod){
89
- if ( typeof mod.init == "function" )
90
- mod.init.apply(self,[args||{}]);
91
- });
85
+ // Initialize all the modules
86
+ _mods.forEach(function(mod){
87
+ if ( typeof mod.init == "function" )
88
+ mod.init.apply(self,[args||{}]);
89
+ });
92
90
 
93
91
  };
94
92
 
@@ -96,33 +94,33 @@ exports.use = function(modules,args) {
96
94
  // Register a hook
97
95
  exports.hook = function(name,callback){
98
96
 
99
- var
100
- self = this;
101
-
102
- if ( !name || !callback )
103
- return;
104
- if ( name.match(/^request(.+)$/i) )
105
- name = RegExp.$1;
106
- if ( name == "writehead" )
107
- name = 'beforewritedata';
108
- else if ( name == "writedata" )
109
- name = 'beforefinish';
110
-
111
- // Hook does not exit? Ciao!
112
- if ( !self.hooks[name.toLowerCase()] )
113
- return;
114
-
115
- // Register the callback
116
- self.hooks[name.toLowerCase()].push(callback);
117
-
118
- // Uppercase hooks are 'global' (to being set on new servers)
119
- if ( name.toUpperCase() == name ) {
120
- name = name.toLowerCase();
121
- if ( !self.globalHooks[name] )
122
- self.globalHooks[name] = [];
123
- // Register the callback
124
- self.globalHooks[name].push(callback);
125
- }
97
+ var
98
+ self = this;
99
+
100
+ if ( !name || !callback )
101
+ return;
102
+ if ( name.match(/^request(.+)$/i) )
103
+ name = RegExp.$1;
104
+ if ( name == "writehead" )
105
+ name = 'beforewritedata';
106
+ else if ( name == "writedata" )
107
+ name = 'beforefinish';
108
+
109
+ // Hook does not exit? Ciao!
110
+ if ( !self.hooks[name.toLowerCase()] )
111
+ return;
112
+
113
+ // Register the callback
114
+ self.hooks[name.toLowerCase()].push(callback);
115
+
116
+ // Uppercase hooks are 'global' (to being set on new servers)
117
+ if ( name.toUpperCase() == name ) {
118
+ name = name.toLowerCase();
119
+ if ( !self.globalHooks[name] )
120
+ self.globalHooks[name] = [];
121
+ // Register the callback
122
+ self.globalHooks[name].push(callback);
123
+ }
126
124
 
127
125
  };
128
126
 
@@ -130,66 +128,66 @@ exports.hook = function(name,callback){
130
128
  // Fires a hook
131
129
  exports._fireHook = function(self,name,args,callback) {
132
130
 
133
- var
134
- sentBeforeHook = (args.length > 1 && args[1]._sent),
135
- sentDuringHook,
136
- _allHooks = (self.hooks[name] || []).slice(0),
137
- req = (args.length > 0) ? args[0] : null;
131
+ var
132
+ sentBeforeHook = (args.length > 1 && args[1]._sent),
133
+ sentDuringHook,
134
+ _allHooks = (self.hooks[name] || []).slice(0),
135
+ req = (args.length > 0) ? args[0] : null;
138
136
 
139
- // Does the request have a hook declaration ?
140
- if ( req && req._route && typeof req._route['#'+name] == "function" )
141
- _allHooks.push(args[0]._route['#'+name]);
137
+ // Does the request have a hook declaration ?
138
+ if ( req && req._route && typeof req._route['#'+name] == "function" )
139
+ _allHooks.push(args[0]._route['#'+name]);
142
140
 
143
- // No hooks? Ciao!
144
- if ( _allHooks.length == 0 ) {
145
- // Process the request normally
146
- return callback(null);
147
- }
141
+ // No hooks? Ciao!
142
+ if ( _allHooks.length == 0 ) {
143
+ // Process the request normally
144
+ return callback(null);
145
+ }
148
146
 
149
147
 
150
- // Add the 'self' instance
151
- args.unshift(self);
148
+ // Add the 'self' instance
149
+ args.unshift(self);
152
150
 
153
- // Call the hooks
154
- return series(_allHooks,args,function(err,done){
155
- if ( err ) {
156
- // _log_error("Error calling '"+name+"' hooks: ",err);
157
- return self.json(args[0],args[1],{error:err},500);
158
- }
151
+ // Call the hooks
152
+ return series(_allHooks,args,function(err,done){
153
+ if ( err ) {
154
+ // _log_error("Error calling '"+name+"' hooks: ",err);
155
+ return self.json(args[0],args[1],{error:err},500);
156
+ }
159
157
 
160
- // It's done, or the answer was sent during hook... finito!
161
- sentDuringHook = !sentBeforeHook && (args.length > 1 && args[1]._sent);
162
- if ( done || sentDuringHook )
163
- return;
158
+ // It's done, or the answer was sent during hook... finito!
159
+ sentDuringHook = !sentBeforeHook && (args.length > 1 && args[1]._sent);
160
+ if ( done || sentDuringHook )
161
+ return;
164
162
 
165
- // Continue processing
166
- return callback(err);
167
- });
163
+ // Continue processing
164
+ return callback(err);
165
+ });
168
166
 
169
167
  };
170
168
 
171
169
  // Fires a syncronous hook
172
170
  exports._fireSyncHook = function(self,name,args,callback) {
173
171
 
174
- var
175
- sentBeforeHook = (args.length > 1 && args[1]._sent),
176
- sentDuringHook;
172
+ var
173
+ sentBeforeHook = (args.length > 1 && args[1]._sent),
174
+ sentDuringHook;
177
175
 
178
- // No callbacks, ciao!
179
- if ( !self.hooks[name] || self.hooks[name].length == 0 ) {
180
- // Process the request normally
181
- return callback(null);
182
- }
176
+ // No callbacks, ciao!
177
+ if ( !self.hooks[name] || self.hooks[name].length == 0 ) {
178
+ // Process the request normally
179
+ return callback(null);
180
+ }
183
181
 
184
- // Call the hooks
185
- for ( var x = 0 ; x < self.hooks[name].length ; x++ ) {
186
- var hook = self.hooks[name][x];
187
- var done = hook.apply(self,args);
188
- if ( typeof done == "boolean" && done )
189
- return true;
190
- }
182
+ // Call the hooks
183
+ for ( var x = 0 ; x < self.hooks[name].length ; x++ ) {
184
+ var hook = self.hooks[name][x];
185
+ var done = hook.apply(self,args);
186
+ if ( typeof done == "boolean" && done )
187
+ return true;
188
+ }
191
189
 
192
- return false;
190
+ return false;
193
191
 
194
192
  };
195
193
 
@@ -197,217 +195,217 @@ exports._fireSyncHook = function(self,name,args,callback) {
197
195
  // Start the server
198
196
  exports.start = function(opts, callback){
199
197
 
200
- var
201
- self = this,
202
- args = Array.prototype.slice.call(arguments, 0),
203
- numProcs,
204
- workers = {};
205
-
206
- // Get and validate arguments
207
- if ( typeof opts == "function" ) {
208
- callback = opts;
209
- opts = null;
210
- }
211
- if ( !callback )
212
- callback = function(){};
213
- if ( !opts )
214
- opts = { port: 8080, address: "0.0.0.0" };
215
- self._opts = opts;
216
-
217
- // Defaults
218
- if ( !opts.mimes )
219
- opts.mimes = { 'html': 'text/html', 'htm': 'text/html', 'js': 'text/javascript', 'css': 'text/css', 'gif': 'image/gif', 'jpg': 'image/jpeg', 'png': 'image/png' };
220
- if ( !opts.processes )
221
- opts.processes = 1;
222
-
223
- _log_info("Starting...");
224
-
225
- // Cluster support
226
- numProcs = (opts.processes || 1);
227
- if ( numProcs > 1 ) {
228
- if ( cluster.isMaster ) {
229
- process.title = "Spritz MASTER";
230
- _log_info("Launching "+numProcs+" childs...");
231
- for ( var x = 0 ; x < numProcs ; x++ ) {
232
- var worker = cluster.fork();
233
- _workerSetup(workers,worker);
234
- }
235
-
236
- _log_info("Launched "+numProcs+" childs");
237
- cluster.on('exit',function(worker,code,signal){
238
- delete workers[worker.process.pid];
239
- self._log_error("Process #"+worker.process.pid+" died (signal "+signal+"). Launching other...");
240
- var worker = cluster.fork();
241
- _workerSetup(workers,worker);
242
- });
243
- }
244
- else {
245
- process.title = "Spritz child process";
246
- return _startServer(self,opts,callback);
247
- }
248
- }
249
- else {
250
- return _startServer(self,opts,callback);
251
- }
198
+ var
199
+ self = this,
200
+ args = Array.prototype.slice.call(arguments, 0),
201
+ numProcs,
202
+ workers = {};
203
+
204
+ // Get and validate arguments
205
+ if ( typeof opts == "function" ) {
206
+ callback = opts;
207
+ opts = null;
208
+ }
209
+ if ( !callback )
210
+ callback = function(){};
211
+ if ( !opts )
212
+ opts = { port: 8080, address: "0.0.0.0" };
213
+ self._opts = opts;
214
+
215
+ // Defaults
216
+ if ( !opts.mimes )
217
+ opts.mimes = { 'html': 'text/html', 'htm': 'text/html', 'js': 'text/javascript', 'css': 'text/css', 'gif': 'image/gif', 'jpg': 'image/jpeg', 'png': 'image/png' };
218
+ if ( !opts.processes )
219
+ opts.processes = 1;
220
+
221
+ _log_info("Starting...");
222
+
223
+ // Cluster support
224
+ numProcs = (opts.processes || 1);
225
+ if ( numProcs > 1 ) {
226
+ if ( cluster.isMaster ) {
227
+ process.title = "Spritz MASTER";
228
+ _log_info("Launching "+numProcs+" childs...");
229
+ for ( var x = 0 ; x < numProcs ; x++ ) {
230
+ var worker = cluster.fork();
231
+ _workerSetup(workers,worker);
232
+ }
233
+
234
+ _log_info("Launched "+numProcs+" childs");
235
+ cluster.on('exit',function(worker,code,signal){
236
+ delete workers[worker.process.pid];
237
+ self._log_error("Process #"+worker.process.pid+" died (signal "+signal+"). Launching other...");
238
+ var worker = cluster.fork();
239
+ _workerSetup(workers,worker);
240
+ });
241
+ }
242
+ else {
243
+ process.title = "Spritz child process";
244
+ return _startServer(self,opts,callback);
245
+ }
246
+ }
247
+ else {
248
+ return _startServer(self,opts,callback);
249
+ }
252
250
 
253
251
  };
254
252
 
255
253
  // Setup a worker
256
254
  var _workerSetup = function(list,worker) {
257
255
 
258
- // Add worker to the list
259
- list[worker.process.pid] = worker;
256
+ // Add worker to the list
257
+ list[worker.process.pid] = worker;
260
258
 
261
- // Listen for messages
262
- worker.on('message', function(msg) {
263
- if ( typeof(msg) == "object" && msg.fn == "console.log" ) {
264
- msg.args.unshift("#"+worker.process.pid+":\t");
265
- console.log.apply(console,msg.args);
266
- }
267
- });
259
+ // Listen for messages
260
+ worker.on('message', function(msg) {
261
+ if ( typeof(msg) == "object" && msg.fn == "console.log" ) {
262
+ msg.args.unshift("#"+worker.process.pid+":\t");
263
+ console.log.apply(console,msg.args);
264
+ }
265
+ });
268
266
 
269
267
  }
270
268
 
271
269
  // Start the HTTP server
272
270
  var _startServer = function(self, opts, callback){
273
271
 
274
- var
275
- server,
276
- iface,
277
- _handleRequest = function(req,res){
278
- handleRequest(self,req,res);
279
- };
280
-
281
- self.handleRequest = _handleRequest;
282
-
283
- // Do we have a callback?
284
- if ( !callback )
285
- callback = function(){};
286
-
287
- // Decide which server module to use
288
- iface = (opts.proto == 'fastcgi') ? require('fastcgi-server') :
289
- (opts.proto == 'https') ? https :
290
- http;
291
-
292
- // Listen
293
- if ( opts.port == null )
294
- opts.port = (opts.proto == "https") ? 443 : 8080;
295
- if ( opts.port ) {
296
- server = iface.createServer(_handleRequest).listen(opts.port || 8080,opts.address || "0.0.0.0",callback);
297
- _log_info("Listening on "+(opts.address || "0.0.0.0")+":"+opts.port);
298
- }
299
- else if ( opts.address && opts.address.match(/\//) ) {
300
- server = self.createServer(_handleRequest).listen(opts.address,callback);
301
- _log_info("Listening on "+opts.address+" UNIX domain socket");
302
- }
303
- else {
304
- _log_warn("Don't know how to listen");
305
- }
306
- return server;
272
+ var
273
+ server,
274
+ iface,
275
+ _handleRequest = function(req,res){
276
+ handleRequest(self,req,res);
277
+ };
278
+
279
+ self.handleRequest = _handleRequest;
280
+
281
+ // Do we have a callback?
282
+ if ( !callback )
283
+ callback = function(){};
284
+
285
+ // Decide which server module to use
286
+ iface = (opts.proto == 'fastcgi') ? require('fastcgi-server') :
287
+ (opts.proto == 'https') ? https :
288
+ http;
289
+
290
+ // Listen
291
+ if ( opts.port == null )
292
+ opts.port = (opts.proto == "https") ? 443 : 8080;
293
+ if ( opts.port ) {
294
+ server = iface.createServer(_handleRequest).listen(opts.port || 8080,opts.address || "0.0.0.0",callback);
295
+ _log_info("Listening on "+(opts.address || "0.0.0.0")+":"+opts.port);
296
+ }
297
+ else if ( opts.address && opts.address.match(/\//) ) {
298
+ server = self.createServer(_handleRequest).listen(opts.address,callback);
299
+ _log_info("Listening on "+opts.address+" UNIX domain socket");
300
+ }
301
+ else {
302
+ _log_warn("Don't know how to listen");
303
+ }
304
+ return server;
307
305
 
308
306
  };
309
307
 
310
308
  // Handle a request
311
309
  var handleRequest = function(self,req,res) {
312
310
 
313
- var
314
- now = new Date();
315
-
316
- // Request just arrived, fire the hook
317
- return self._fireHook(self,'arrive',[req,res,{}],function(){
318
-
319
- // Request related values
320
- req._cType = req.headers['content-type'] ? req.headers['content-type'].toString().replace(/;.*/g,"") : "unknown/unknown";
321
- req.xRequestID = (self.reqSeq++) + "-" + process.pid.toString() + "-" + now.getYear()+now.getMonth()+now.getDay()+now.getHours()+now.getMinutes();
322
- req.xConnectDate = now;
323
- req.xRemoteAddr = req.connection.remoteAddress || ((req.client && req.client._peername) ? req.client._peername.address : "0.0.0.0");
324
- if ( req.xRemoteAddr == "127.0.0.1" && req.headers['x-forwarded-for'] && req.headers['x-forwarded-for'].match(/^(\d{1,3}\.){3}\d{1,3}$/) ) {
325
- req.xDirectRemoteAddr = req.xRemoteAddr;
326
- req.xRemoteAddr = req.headers['x-forwarded-for'];
327
- }
328
-
329
- // Response related values
330
- res._cookies = {};
331
- res.setCookie = function(n,v,o) {
332
- res._cookies[n] = { value: v, opts: o };
333
- };
334
-
335
- // Request arguments
336
- req.args = {};
337
- req.originalURL = req.url;
338
- if ( req.url.match(/^(.*?)\?(.*)$/) ) {
339
- req.url = RegExp.$1;
340
- req.urlNoArgs = RegExp.$1;
341
- req.args = qs.parse(RegExp.$2);
342
- }
343
-
344
- // POST data reader
345
- req.readPOSTData = function(cb){cb(null,{});};
346
- if ( req.method == "POST" ) {
347
- req.readPOSTData = function(cb){
348
- return readPOSTData(self,req,function(err){
349
- return cb(err,self.POSTdata);
350
- });
351
- };
352
- }
353
-
354
- // The logging flags
355
- req.xLoggingFlags = [];
356
-
357
- // Finished read request
358
- return self._fireHook(self,'readheaders',[req,res,{}],function(){
359
-
360
- // Route request
361
- return self._route(req,res);
362
-
363
- });
364
- });
311
+ var
312
+ now = new Date();
313
+
314
+ // Request just arrived, fire the hook
315
+ return self._fireHook(self,'arrive',[req,res,{}],function(){
316
+
317
+ // Request related values
318
+ req._cType = req.headers['content-type'] ? req.headers['content-type'].toString().replace(/;.*/g,"") : "unknown/unknown";
319
+ req.xRequestID = (self.reqSeq++) + "-" + process.pid.toString() + "-" + now.getYear()+now.getMonth()+now.getDay()+now.getHours()+now.getMinutes();
320
+ req.xConnectDate = now;
321
+ req.xRemoteAddr = req.connection.remoteAddress || ((req.client && req.client._peername) ? req.client._peername.address : "0.0.0.0");
322
+ if ( req.xRemoteAddr == "127.0.0.1" && req.headers['x-forwarded-for'] && req.headers['x-forwarded-for'].match(/^(\d{1,3}\.){3}\d{1,3}$/) ) {
323
+ req.xDirectRemoteAddr = req.xRemoteAddr;
324
+ req.xRemoteAddr = req.headers['x-forwarded-for'];
325
+ }
326
+
327
+ // Response related values
328
+ res._cookies = {};
329
+ res.setCookie = function(n,v,o) {
330
+ res._cookies[n] = { value: v, opts: o };
331
+ };
332
+
333
+ // Request arguments
334
+ req.args = {};
335
+ req.originalURL = req.url;
336
+ if ( req.url.match(/^(.*?)\?(.*)$/) ) {
337
+ req.url = RegExp.$1;
338
+ req.urlNoArgs = RegExp.$1;
339
+ req.args = qs.parse(RegExp.$2);
340
+ }
341
+
342
+ // POST data reader
343
+ req.readPOSTData = function(cb){cb(null,{});};
344
+ if ( req.method == "POST" || req.method == "PUT" ) {
345
+ req.readPOSTData = function(cb){
346
+ return readPOSTData(self,req,function(err){
347
+ return cb(err,self.POSTdata);
348
+ });
349
+ };
350
+ }
351
+
352
+ // The logging flags
353
+ req.xLoggingFlags = [];
354
+
355
+ // Finished read request
356
+ return self._fireHook(self,'readheaders',[req,res,{}],function(){
357
+
358
+ // Route request
359
+ return self._route(req,res);
360
+
361
+ });
362
+ });
365
363
 
366
364
  };
367
365
 
368
366
  // Read data from POST and parse it
369
367
  var readPOSTData = function(self,req,callback) {
370
368
 
371
- // POST data already read, don't do it again
372
- if ( req._readPOSTData )
373
- return callback(null,req);
374
- req._readPOSTData = true;
375
-
376
- // multipart/form-data or just a regular urlencoded form?
377
- if ( req._cType.match(/^multipart\/form\-data/) ) {
378
- try {
379
- var
380
- form = new formidable.IncomingForm();
381
-
382
- form.parse(req,function(err,args,files){
383
- if ( err )
384
- return callback(err,false);
385
-
386
- req.POSTargs = args;
387
- req.POSTfiles = files;
388
- return callback(null,req);
389
- });
390
- }
391
- catch(ex) {
392
- return callback(ex,null);
393
- }
394
- }
395
- else {
396
- req.setEncoding("utf-8");
397
- var buf = "";
398
- req.on('data',function(chunk){ buf += chunk; });
399
- req.on('end',function(){
400
- if ( req._cType == "application/json" ) {
401
- try { req.POSTjson = JSON.parse(buf); } catch(ex){ _log_error("Error parsing POST JSON: ",ex.toString(),"JSON: ",buf); }
402
- }
403
- else {
404
- req.POSTargs = qs.parse(buf);
405
- if ( req.POSTargs['json'] )
406
- try { req.POSTjson = JSON.parse(req.POSTargs['json']); } catch(ex){ _log_error("Error parsing POST JSON: ",ex.toString(),"JOSN: ",req.POSTargs['json']); }
407
- }
408
- return callback(null,req);
409
- });
410
- }
369
+ // POST data already read, don't do it again
370
+ if ( req._readPOSTData )
371
+ return callback(null,req);
372
+ req._readPOSTData = true;
373
+
374
+ // multipart/form-data or just a regular urlencoded form?
375
+ if ( req._cType.match(/^multipart\/form\-data/) ) {
376
+ try {
377
+ var
378
+ form = new formidable.IncomingForm();
379
+
380
+ form.parse(req,function(err,args,files){
381
+ if ( err )
382
+ return callback(err,false);
383
+
384
+ req.POSTargs = args;
385
+ req.POSTfiles = files;
386
+ return callback(null,req);
387
+ });
388
+ }
389
+ catch(ex) {
390
+ return callback(ex,null);
391
+ }
392
+ }
393
+ else {
394
+ req.setEncoding("utf-8");
395
+ var buf = "";
396
+ req.on('data',function(chunk){ buf += chunk; });
397
+ req.on('end',function(){
398
+ if ( req._cType == "application/json" ) {
399
+ try { req.POSTjson = JSON.parse(buf); } catch(ex){ _log_error("Error parsing POST JSON: ",ex.toString(),"JSON: ",buf); }
400
+ }
401
+ else {
402
+ req.POSTargs = qs.parse(buf);
403
+ if ( req.POSTargs['json'] )
404
+ try { req.POSTjson = JSON.parse(req.POSTargs['json']); } catch(ex){ _log_error("Error parsing POST JSON: ",ex.toString(),"JOSN: ",req.POSTargs['json']); }
405
+ }
406
+ return callback(null,req);
407
+ });
408
+ }
411
409
 
412
410
  };
413
411
 
@@ -415,54 +413,54 @@ var readPOSTData = function(self,req,callback) {
415
413
  // Generic route handler
416
414
  exports.on = function(r,opts,reqHandler){
417
415
 
418
- var
419
- self = this,
420
- args = Array.prototype.slice.call(arguments, 0);
421
-
422
- // Get arguments
423
- r = args.shift();
424
- reqHandler = args.pop();
425
-
426
- // Is it a hook ? Set it using the hook()
427
- if ( typeof r == "string" && r.match(/^#(\w+)$/) )
428
- return self.hook(RegExp.$1,reqHandler);
429
-
430
-
431
- // Merge options with the defaults
432
- opts = _merge({
433
- // method: "GET",
434
- handler: reqHandler,
435
- expr: r
436
- },args.shift()||{});
437
-
438
- // Fire the setroute hook
439
- if ( self._fireSyncHook(self,'setroute',[opts]) ) {
440
- // Setting route was aborted
441
- return;
442
- }
443
-
444
- var routes = (r instanceof Array) ? r : [r];
445
-
446
- routes.forEach(function(r) {
447
- // Is it a RegExp ?
448
- if ( r instanceof RegExp ) {
449
- // Register the route on the RegExp route list
450
- self.rxRoutes.push([r,opts]);
451
- }
452
- else if ( typeof r == "string" ) {
453
- // Register the route on the string route list
454
- self.routes[(opts.method?opts.method.toUpperCase()+" ! ":"")+r] = opts;
455
- }
456
- else if ( typeof r == "number" ) {
457
- r = r.toString();
458
- if ( !self.statusRoutes[r] )
459
- self.statusRoutes[r] = [];
460
- // Register the route on the status route list
461
- self.statusRoutes[r].push(opts);
462
- }
463
- else
464
- throw new Error("Don't know what to do with route '"+r+"'");
465
- });
416
+ var
417
+ self = this,
418
+ args = Array.prototype.slice.call(arguments, 0);
419
+
420
+ // Get arguments
421
+ r = args.shift();
422
+ reqHandler = args.pop();
423
+
424
+ // Is it a hook ? Set it using the hook()
425
+ if ( typeof r == "string" && r.match(/^#(\w+)$/) )
426
+ return self.hook(RegExp.$1,reqHandler);
427
+
428
+
429
+ // Merge options with the defaults
430
+ opts = _merge({
431
+ // method: "GET",
432
+ handler: reqHandler,
433
+ expr: r
434
+ },args.shift()||{});
435
+
436
+ // Fire the setroute hook
437
+ if ( self._fireSyncHook(self,'setroute',[opts]) ) {
438
+ // Setting route was aborted
439
+ return;
440
+ }
441
+
442
+ var routes = (r instanceof Array) ? r : [r];
443
+
444
+ routes.forEach(function(r) {
445
+ // Is it a RegExp ?
446
+ if ( r instanceof RegExp ) {
447
+ // Register the route on the RegExp route list
448
+ self.rxRoutes.push([r,opts]);
449
+ }
450
+ else if ( typeof r == "string" ) {
451
+ // Register the route on the string route list
452
+ self.routes[(opts.method?opts.method.toUpperCase()+" ! ":"")+r] = opts;
453
+ }
454
+ else if ( typeof r == "number" ) {
455
+ r = r.toString();
456
+ if ( !self.statusRoutes[r] )
457
+ self.statusRoutes[r] = [];
458
+ // Register the route on the status route list
459
+ self.statusRoutes[r].push(opts);
460
+ }
461
+ else
462
+ throw new Error("Don't know what to do with route '"+r+"'");
463
+ });
466
464
 
467
465
  };
468
466
 
@@ -470,116 +468,113 @@ exports.on = function(r,opts,reqHandler){
470
468
  // Route a request
471
469
  var _route = function(req,res) {
472
470
 
473
- var
474
- self = this,
475
- routeOpts,
476
- matchedRoute;
477
-
478
- // String routes
479
- if ( self.routes[req.method+" ! "+req.url] != null ) {
480
- routeOpts = self.routes[req.method+" ! "+req.url];
481
- }
482
- else if ( self.routes[req.url] ) {
483
- routeOpts = self.routes[req.url];
484
- }
485
-
486
- // RegExp routes
487
- else {
488
- for ( var x = 0 ; x < self.rxRoutes.length ; x++ ) {
489
- if ( req.url.match(self.rxRoutes[x][0]) && (!self.rxRoutes[x][1].method || (self.rxRoutes[x][1].method.toUpperCase() == req.method)) ) {
490
- matchedRoute = self.rxRoutes[x][0];
491
- routeOpts = self.rxRoutes[x][1];
492
- break;
493
- }
494
- }
495
- }
496
-
497
- // Still no handler? 404...
498
- if ( !routeOpts ) {
499
- res.statusCode = 404;
500
- // Fire read hook
501
- return self._fireHook(self,'read',[req,res,{}],function(){
502
- return self._routeStatus(req,res,false);
503
- });
504
- }
505
-
506
- // Read POST data ?
507
- return _if ( !routeOpts.dontReadPOSTData,
508
- function(next){
509
- req.readPOSTData(next);
510
- },
511
- function(err){
512
- if ( err )
513
- _log_error("Error reading request POST data: ",err);
514
-
515
- // Fire read hook
516
- return self._fireHook(self,'read',[req,res,{}],function(){
517
-
518
- // Fire find route hook
519
- req._route = routeOpts;
520
- return self._fireHook(self,'findroute',[req,res,{route: routeOpts}],function(){
521
-
522
- // Set the RegExp object
523
- if ( matchedRoute )
524
- req.url.match(self.rxRoutes[x][0]);
525
-
526
- // Call the route handler
527
- return routeOpts.handler(req,res);
528
- });
529
- });
530
- }
531
- );
471
+ var
472
+ self = this,
473
+ routeOpts,
474
+ matchedRoute;
475
+
476
+ // String routes
477
+ if ( self.routes[req.method+" ! "+req.url] != null ) {
478
+ routeOpts = self.routes[req.method+" ! "+req.url];
479
+ }
480
+ else if ( self.routes[req.url] ) {
481
+ routeOpts = self.routes[req.url];
482
+ }
483
+
484
+ // RegExp routes
485
+ else {
486
+ for ( var x = 0 ; x < self.rxRoutes.length ; x++ ) {
487
+ if ( req.url.match(self.rxRoutes[x][0]) && (!self.rxRoutes[x][1].method || (self.rxRoutes[x][1].method.toUpperCase() == req.method)) ) {
488
+ matchedRoute = self.rxRoutes[x][0];
489
+ routeOpts = self.rxRoutes[x][1];
490
+ break;
491
+ }
492
+ }
493
+ }
494
+
495
+ // Still no handler? 404...
496
+ if ( !routeOpts ) {
497
+ res.statusCode = 404;
498
+ // Fire read hook
499
+ return self._fireHook(self,'read',[req,res,{}],function(){
500
+ return self._routeStatus(req,res,false);
501
+ });
502
+ }
503
+
504
+ // Read POST data ?
505
+ return _if ( !routeOpts.dontReadPOSTData,
506
+ function(next){
507
+ req.readPOSTData(next);
508
+ },
509
+ function(err){
510
+ if ( err )
511
+ _log_error("Error reading request POST data: ",err);
512
+
513
+ // Fire read hook
514
+ return self._fireHook(self,'read',[req,res,{}],function(){
515
+
516
+ // Fire find route hook
517
+ req._route = routeOpts;
518
+ return self._fireHook(self,'findroute',[req,res,{route: routeOpts}],function(){
519
+
520
+ // Set the RegExp object
521
+ if ( matchedRoute )
522
+ req.url.match(self.rxRoutes[x][0]);
523
+
524
+ // Call the route handler
525
+ return routeOpts.handler(req,res);
526
+ });
527
+ });
528
+ }
529
+ );
532
530
 
533
531
  };
534
532
  exports._route = _route;
535
533
 
536
534
 
537
535
  // Route a status occurrence
538
- var _routeStatus = function(req,res,alreadyServed,headers) {
539
-
540
- var
541
- self = this,
542
- ans,
543
- routes;
544
-
545
- // Inside a status route handler ?
546
- if ( req.onStatusRouteH )
547
- return;
548
-
549
- // Already served ? Mark it on request, so future route handlers can take this in consideration
550
- req.served = alreadyServed;
551
-
552
- // Do we have a status handler ?
553
- routes = self.statusRoutes[res.statusCode.toString()];
554
- if ( routes ) {
555
- req.onStatusRouteH = true;
556
- return mapSeries(routes,self,
557
- function(r,next){
558
- return self._fireHook(self,'findroute',[req,res,{route:r}],next);
559
- },
560
- function(){
561
- var
562
- handlers = routes.map(function(r){return r.handler});
563
-
564
- // Call the handlers
565
- return series(handlers,[self,req,res],function(err,done){});
566
- }
567
- );
568
- }
569
-
570
- // Already served ? Ciao!
571
- if ( alreadyServed )
572
- return;
573
-
574
- // No.. default status handler
575
- ans = (res.statusCode == 404) ? { error: 'No route for this request type' } :
576
- (res.statusCode == 401) ? { warn: 'Authentication required' } :
577
- (res.statusCode >= 400) ? { error: 'Got error '+res.statusCode } :
578
- { info: 'Returning status '+res.statusCode };
579
-
580
- // Something to answer? Answer..!
581
- if ( ans && !alreadyServed )
582
- return self.json(req,res,ans,res.statusCode,headers);
536
+ var _routeStatus = function(req,res,headers) {
537
+
538
+ var
539
+ self = this,
540
+ ans,
541
+ routes;
542
+
543
+ // Inside a status route handler ?
544
+ if ( req.onStatusRouteH )
545
+ return;
546
+
547
+ // Do we have a status handler ?
548
+ routes = self.statusRoutes[res.statusCode.toString()];
549
+ if ( routes ) {
550
+ req.onStatusRouteH = true;
551
+ return mapSeries(routes,self,
552
+ function(r,next){
553
+ return self._fireHook(self,'findroute',[req,res,{route:r}],next);
554
+ },
555
+ function(){
556
+ var
557
+ handlers = routes.map(function(r){return r.handler});
558
+
559
+ // Call the handlers
560
+ return series(handlers,[self,req,res],function(err,done){});
561
+ }
562
+ );
563
+ }
564
+
565
+ // Already served ? Ciao!
566
+ if ( req.served )
567
+ return;
568
+
569
+ // No.. default status handler
570
+ ans = (res.statusCode == 404) ? { error: 'No route for this request type' } :
571
+ (res.statusCode == 401) ? { warn: 'Authentication required' } :
572
+ (res.statusCode >= 400) ? { error: 'Got error '+res.statusCode } :
573
+ { info: 'Returning status '+res.statusCode };
574
+
575
+ // Something to answer? Answer..!
576
+ if ( ans && !req.served )
577
+ return self.json(req,res,ans,res.statusCode,headers);
583
578
 
584
579
  };
585
580
  exports._routeStatus = _routeStatus;
@@ -587,17 +582,20 @@ exports._routeStatus = _routeStatus;
587
582
 
588
583
  // Write the head of an HTTP response
589
584
  var _writeHead = function(self,req,res,status,headers,callback){
585
+ if (req.served)
586
+ return;
587
+ req.served = true;
590
588
 
591
- var
592
- headObj = {status: status, headers: headers};
589
+ var
590
+ headObj = {status: status, headers: headers};
593
591
 
594
- return self._fireHook(self,'beforewritehead',[req,res,headObj],function(){
595
- res.writeHead(headObj.status,headObj.headers);
596
- // Mark on the answer that we sent it
597
- res._sent = true;
598
- res.statusCode = headObj.status;
599
- return callback();
600
- });
592
+ return self._fireHook(self,'beforewritehead',[req,res,headObj],function(){
593
+ res.writeHead(headObj.status,headObj.headers);
594
+ // Mark on the answer that we sent it
595
+ res._sent = true;
596
+ res.statusCode = headObj.status;
597
+ return callback();
598
+ });
601
599
 
602
600
  };
603
601
  exports._writeHead = _writeHead;
@@ -605,27 +603,27 @@ exports._writeHead = _writeHead;
605
603
  // Write the head of an HTTP response
606
604
  var _writeData = function(self,req,res,data,end,callback){
607
605
 
608
- var
609
- dataObj = { data: data, end: end };
606
+ var
607
+ dataObj = { data: data, end: end };
610
608
 
611
- return self._fireHook(self,'beforewritedata',[req,res,dataObj],function(){
609
+ return self._fireHook(self,'beforewritedata',[req,res,dataObj],function(){
612
610
 
613
- // Just writing...
614
- if ( !dataObj.end ) {
615
- res.write(dataObj.data);
616
- return callback();
617
- }
611
+ // Just writing...
612
+ if ( !dataObj.end ) {
613
+ res.write(dataObj.data);
614
+ return callback();
615
+ }
618
616
 
619
- // Write and end
620
- return self._fireHook(self,'beforefinish',[req,res,dataObj],function(){
621
- res.end(dataObj.data);
617
+ // Write and end
618
+ return self._fireHook(self,'beforefinish',[req,res,dataObj],function(){
619
+ res.end(dataObj.data);
622
620
 
623
- // Finish
624
- return self._fireHook(self,'finish',[req,res,{}],function(){
625
- return callback();
626
- });
627
- });
628
- });
621
+ // Finish
622
+ return self._fireHook(self,'finish',[req,res,{}],function(){
623
+ return callback();
624
+ });
625
+ });
626
+ });
629
627
 
630
628
  }
631
629
  exports._writeData = _writeData;
@@ -633,18 +631,18 @@ exports._writeData = _writeData;
633
631
  // Pipe a stream into an HTTP response
634
632
  var _pipeStream = function(self,req,res,stream,callback){
635
633
 
636
- var
637
- pr;
634
+ var
635
+ pr;
638
636
 
639
- return self._fireHook(self,'beforewritedata',[req,res,stream],function(){
637
+ return self._fireHook(self,'beforewritedata',[req,res,stream],function(){
640
638
 
641
- // Pipe the stream
642
- pr = stream.pipe(res);
643
- stream.on('end',function(){
644
- callback(null,true);
645
- });
639
+ // Pipe the stream
640
+ pr = stream.pipe(res);
641
+ stream.on('end',function(){
642
+ callback(null,true);
643
+ });
646
644
 
647
- });
645
+ });
648
646
 
649
647
  };
650
648
  exports._pipeStream = _pipeStream;
@@ -653,61 +651,61 @@ exports._pipeStream = _pipeStream;
653
651
  // Answer with a text string
654
652
  exports.text = function(req,res,content,status,headers,callback) {
655
653
 
656
- var
657
- self = this,
658
- length = Buffer.byteLength(content,'utf8'),
659
- _headers = _merge({
660
- 'content-type': 'text/plain; charset=utf-8',
661
- 'content-length': length,
662
- 'date': new Date().toUTCString()
663
- },headers,true);
664
-
665
- // Set the status code
666
- res.statusCode = status || 200;
667
-
668
- // Send data
669
- return _writeHead(self,req,res,res.statusCode,_headers,function(){
670
- return _writeData(self,req,res,content,true,function(){
671
- // Log
672
- self._access_log(req,res,length);
673
-
674
- // Report status
675
- self._routeStatus(req,res,true);
676
-
677
- // Call the callback
678
- if ( callback )
679
- callback(null,true);
680
- });
681
- });
654
+ var
655
+ self = this,
656
+ length = Buffer.byteLength(content,'utf8'),
657
+ _headers = _merge({
658
+ 'content-type': 'text/plain; charset=utf-8',
659
+ 'content-length': length,
660
+ 'date': new Date().toUTCString()
661
+ },headers,true);
662
+
663
+ // Set the status code
664
+ res.statusCode = status || 200;
665
+
666
+ // Send data
667
+ return _writeHead(self,req,res,res.statusCode,_headers,function(){
668
+ return _writeData(self,req,res,content,true,function(){
669
+ // Log
670
+ self._access_log(req,res,length);
671
+
672
+ // Report status
673
+ self._routeStatus(req,res,true);
674
+
675
+ // Call the callback
676
+ if ( callback )
677
+ callback(null,true);
678
+ });
679
+ });
682
680
 
683
681
  };
684
682
 
685
683
  // Answer with JSON
686
684
  exports.json = function(req,res,content,status,headers,pretty,callback) {
687
685
 
688
- var
689
- strfyArgs = [content];
686
+ var
687
+ strfyArgs = [content];
690
688
 
691
- if ( pretty && typeof pretty == "function" ) {
692
- callback = pretty;
693
- pretty = false;
694
- }
695
- if ( pretty )
696
- strfyArgs.push(null,4);
689
+ if ( pretty && typeof pretty == "function" ) {
690
+ callback = pretty;
691
+ pretty = false;
692
+ }
693
+ if ( pretty )
694
+ strfyArgs.push(null,4);
697
695
 
698
- // Build JSON content
699
- content = JSON.stringify.apply(null,strfyArgs);
696
+ // Build JSON content
697
+ content = JSON.stringify.apply(null,strfyArgs);
700
698
 
701
- // JSONP ?
702
- if ( req.args.callback )
703
- content = req.args.callback.toString() + "(" + content.replace(/[\u00a0\u2000-\u203f]/g,"") + ");";
699
+ // JSONP ?
700
+ if ( req.args.callback )
701
+ content = req.args.callback.toString() + "(" + content.replace(/[\u00a0\u2000-\u203f]/g,"") + ");";
704
702
 
705
- // Send the data
706
- this.text(req,res,content,status,_merge({"content-type":"application/json; charset=utf-8"},headers,true));
703
+ // Send the data
704
+ this.text(req,res,content,status,_merge({"content-type":"application/json; charset=utf-8"},headers,true));
707
705
 
708
- // Call the callback
709
- if ( callback )
710
- callback(null,true);
706
+ // Call the callback
707
+ if ( callback )
708
+ callback(null,true);
711
709
 
712
710
  };
713
711
 
@@ -715,7 +713,7 @@ exports.json = function(req,res,content,status,headers,pretty,callback) {
715
713
  // Template
716
714
  exports.template = function(req,res,file,data,status,headers,callback){
717
715
 
718
- throw new Exception("No templating module was loaded. Use spritz.use()");
716
+ throw new Exception("No templating module was loaded. Use spritz.use()");
719
717
 
720
718
  };
721
719
 
@@ -735,100 +733,100 @@ var _log_error = function() {
735
733
  return _log("ERROR: ",arguments);
736
734
  }
737
735
  var _log = function(type,args) {
738
- var
739
- _args = [type],
740
- _keys;
741
-
742
- // Convert arguments into array - old style
743
- _keys = args ? Object.keys(args) : [];
744
- _keys.forEach(function(num){
745
- _args.push(args[num]);
746
- });
747
-
748
- // Master, send directly to console
749
- if ( cluster.isMaster ) {
750
- _args.unshift("MASTER:\t");
751
- console.log.apply(console,_args);
752
- }
753
- // Children send via cluster messaging system
754
- else {
755
- // Send to the master process, so we avoid problems with many processes writing on the same file
756
- process.send({fn:'console.log',args: _args});
757
- }
736
+ var
737
+ _args = [type],
738
+ _keys;
739
+
740
+ // Convert arguments into array - old style
741
+ _keys = args ? Object.keys(args) : [];
742
+ _keys.forEach(function(num){
743
+ _args.push(args[num]);
744
+ });
745
+
746
+ // Master, send directly to console
747
+ if ( cluster.isMaster ) {
748
+ _args.unshift("MASTER:\t");
749
+ console.log.apply(console,_args);
750
+ }
751
+ // Children send via cluster messaging system
752
+ else {
753
+ // Send to the master process, so we avoid problems with many processes writing on the same file
754
+ process.send({fn:'console.log',args: _args});
755
+ }
758
756
  };
759
- exports._log = _log;
760
- exports._log_info = _log_info;
761
- exports._log_warn = _log_warn;
762
- exports._log_error = _log_error;
757
+ exports._log = _log;
758
+ exports._log_info = _log_info;
759
+ exports._log_warn = _log_warn;
760
+ exports._log_error = _log_error;
763
761
 
764
762
  // Access log
765
763
  exports._access_log = function(req,res,length) {
766
- var
767
- timeSpent = new Date().getTime() - req.xConnectDate.getTime(),
768
- flags = "";
764
+ var
765
+ timeSpent = new Date().getTime() - req.xConnectDate.getTime(),
766
+ flags = "";
769
767
 
770
- if ( req.xLoggingFlags && req.xLoggingFlags.length > 0 )
771
- flags = " "+req.xLoggingFlags.join('');
768
+ if ( req.xLoggingFlags && req.xLoggingFlags.length > 0 )
769
+ flags = " "+req.xLoggingFlags.join('');
772
770
 
773
- _log(req.xRemoteAddr+(req.xDirectRemoteAddr?"/"+req.xDirectRemoteAddr:"")+" - "+req.xRequestID+" ["+req.xConnectDate.toString()+"] \""+req.method+" "+(req.originalURL || req.url)+" HTTP/"+req.httpVersionMajor+"."+req.httpVersionMajor+"\" "+res.statusCode+" "+(length||"-")+" "+(timeSpent / 1000).toString()+flags);
771
+ _log(req.xRemoteAddr+(req.xDirectRemoteAddr?"/"+req.xDirectRemoteAddr:"")+" - "+req.xRequestID+" ["+req.xConnectDate.toString()+"] \""+req.method+" "+(req.originalURL || req.url)+" HTTP/"+req.httpVersionMajor+"."+req.httpVersionMajor+"\" "+res.statusCode+" "+(length||"-")+" "+(timeSpent / 1000).toString()+flags);
774
772
  };
775
773
 
776
774
  // Merge 2 objects
777
775
  var _merge = function(a,b,lcProps){
778
- var o = {};
779
- if ( a != null ) {
780
- for ( var p in a )
781
- o[lcProps?p.toLowerCase():p] = a[p];
782
- }
783
- if ( b != null ) {
784
- for ( var p in b )
785
- o[lcProps?p.toLowerCase():p] = b[p];
786
- }
787
- return o;
776
+ var o = {};
777
+ if ( a != null ) {
778
+ for ( var p in a )
779
+ o[lcProps?p.toLowerCase():p] = a[p];
780
+ }
781
+ if ( b != null ) {
782
+ for ( var p in b )
783
+ o[lcProps?p.toLowerCase():p] = b[p];
784
+ }
785
+ return o;
788
786
  };
789
787
  exports._merge = _merge;
790
788
 
791
789
  // Asyncronous if
792
790
  var _if = function(c,a,b) {
793
- return c ? a(b) : b();
791
+ return c ? a(b) : b();
794
792
  };
795
793
 
796
794
 
797
795
  // Call a list of callbacks in series (could eventually be replaced by async.series or async.mapSeries but we don't want to add more dependencies)
798
796
  var series = function(fns,args,callback){
799
- var
800
- _self = args.shift(),
801
- _fns = fns.slice(),
802
- _next = function(){
803
- if ( !_fns.length )
804
- return callback(null,false);
805
- _fns.shift().apply(_self,args);
806
- };
807
-
808
- // Add as last argument our function return handler
809
- args.push(function(err,stop,done){
810
- if ( err )
811
- return callback(err,false);
812
- return (stop || done) ? callback(null,done) : setImmediate(_next);
813
- });
814
-
815
- return _next();
797
+ var
798
+ _self = args.shift(),
799
+ _fns = fns.slice(),
800
+ _next = function(){
801
+ if ( !_fns.length )
802
+ return callback(null,false);
803
+ _fns.shift().apply(_self,args);
804
+ };
805
+
806
+ // Add as last argument our function return handler
807
+ args.push(function(err,stop,done){
808
+ if ( err )
809
+ return callback(err,false);
810
+ return (stop || done) ? callback(null,done) : setImmediate(_next);
811
+ });
812
+
813
+ return _next();
816
814
  };
817
815
 
818
816
  var mapSeries = function(arr,_self,itCb,fiCb){
819
- var
820
- _arr = arr.slice(),
821
- _res = [],
822
- _next = function(err,res){
823
- if ( !_arr.length )
824
- return fiCb(err,_res);
825
- itCb.apply(_self,[_arr.shift(),function(err,res){
826
- if ( err )
827
- return fiCb(err,_res);
828
- _res.push(res);
829
- setImmediate(_next);
830
- }]);
831
- };
817
+ var
818
+ _arr = arr.slice(),
819
+ _res = [],
820
+ _next = function(err,res){
821
+ if ( !_arr.length )
822
+ return fiCb(err,_res);
823
+ itCb.apply(_self,[_arr.shift(),function(err,res){
824
+ if ( err )
825
+ return fiCb(err,_res);
826
+ _res.push(res);
827
+ setImmediate(_next);
828
+ }]);
829
+ };
832
830
  return _next();
833
831
  };
834
832