Skip to content

Commit afb6888

Browse files
committed
Added F.cors() --> beta version.
1 parent 9d1c685 commit afb6888

5 files changed

Lines changed: 378 additions & 10 deletions

File tree

changes.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
======= 1.9.7 (BETA)
2+
3+
- added: F.cors(url, origins, methods, headers, credentials)
4+
15
======= 1.9.6
26

37
- added: MailMessage.manually() and removes auto-sending mail --> works only with `F.mail()` and `controller.Mail()`.

index.js

Lines changed: 274 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ function Framework() {
538538
sitemap: null,
539539
web: [],
540540
files: [],
541+
cors: [],
541542
websockets: [],
542543
middleware: {},
543544
redirects: {},
@@ -607,6 +608,7 @@ function Framework() {
607608
head: 0,
608609
post: 0,
609610
put: 0,
611+
path: 0,
610612
upload: 0,
611613
blocked: 0,
612614
'delete': 0,
@@ -657,6 +659,7 @@ function Framework() {
657659
this._length_files = 0;
658660
this._length_wait = 0;
659661
this._length_themes = 0;
662+
this._length_cors = 0;
660663

661664
this.isVirtualDirectory = false;
662665
this.isTheme = false;
@@ -1078,6 +1081,77 @@ Framework.prototype.restful = function(url, flags, onQuery, onGet, onSave, onDel
10781081
return self;
10791082
};
10801083

1084+
/**
1085+
* Register cors
1086+
* @param {String} url
1087+
* @param {String Array or String} origin
1088+
* @param {String Array or String} methods
1089+
* @param {String Array or String} headers
1090+
* @param {Boolean} credentials
1091+
* @return {Framework}
1092+
*/
1093+
Framework.prototype.cors = function(url, origin, methods, credentials, headers) {
1094+
1095+
var self = this;
1096+
var route = {};
1097+
var tmp;
1098+
1099+
if (typeof(credentials) === STRING || credentials instanceof Array) {
1100+
tmp = headers;
1101+
headers = credentials;
1102+
credentials = tmp;
1103+
}
1104+
1105+
if (typeof(origin) === STRING)
1106+
origin = origin.split(',').trim();
1107+
if (typeof(methods) === STRING)
1108+
methods = methods.split(',').trim();
1109+
if (typeof(headers) === STRING)
1110+
headers = headers.split(',').trim();
1111+
1112+
if (!headers)
1113+
headers = ['*'];
1114+
if (!origin)
1115+
origin = ['*'];
1116+
if (!methods)
1117+
methods = ['*'];
1118+
1119+
route.isASTERIX = url.lastIndexOf('*') !== -1;
1120+
1121+
if (route.isASTERIX)
1122+
url = url.replace('*', '');
1123+
1124+
for (var i = 0, length = origin.length; i < length; i++)
1125+
origin[i] = origin[i].toLowerCase();
1126+
1127+
for (var i = 0, length = headers.length; i < length; i++)
1128+
headers[i] = headers[i].toLowerCase();
1129+
1130+
for (var i = 0, length = methods.length; i < length; i++)
1131+
methods[i] = methods[i].toUpperCase();
1132+
1133+
route.url = framework_internal.routeSplitCreate(framework_internal.encodeUnicodeURL(url.trim()));
1134+
route.origin = origin.indexOf('*') === -1 ? origin : null;
1135+
route.methods = methods.indexOf('*') === -1 ? methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATH', 'HEAD', 'OPTIONS'];
1136+
route.headers = headers.indexOf('*') === -1 ? headers : null;
1137+
route.credentials = credentials;
1138+
1139+
self.routes.cors.push(route);
1140+
self._length_cors = self.routes.cors.length;
1141+
1142+
self.routes.cors.sort(function(a, b) {
1143+
var al = a.url.length;
1144+
var bl = b.url.length;
1145+
if (al > bl)
1146+
return -1;
1147+
if (al < bl)
1148+
return 1;
1149+
return a.isASTERIX && b.isASTERIX ? 1 : 0;
1150+
});
1151+
1152+
return self;
1153+
};
1154+
10811155
/**
10821156
* Add a route
10831157
* @param {String} url
@@ -6254,39 +6328,94 @@ Framework.prototype._request_continue = function(req, res, headers, protocol) {
62546328
}
62556329
}
62566330

6331+
var skipCors = !req.headers['origin'] || !self._length_cors;
62576332
req.flags = flags;
6258-
6259-
// call event request
62606333
self.emit('request-begin', req, res);
62616334

62626335
switch (first) {
62636336
case 'G':
62646337
self.stats.request.get++;
6265-
new Subscribe(self, req, res, 0).end();
6338+
6339+
if (skipCors) {
6340+
new Subscribe(self, req, res, 0).end();
6341+
return self;
6342+
}
6343+
6344+
self._cors(req, res, function(req, res) {
6345+
new Subscribe(framework, req, res, 0).end();
6346+
});
62666347
return self;
6348+
62676349
case 'O':
62686350
self.stats.request.options++;
6269-
new Subscribe(self, req, res, 0).end();
6351+
6352+
if (skipCors) {
6353+
new Subscribe(framework, req, res, 0).end();
6354+
return self;
6355+
}
6356+
6357+
self._cors(req, res);
62706358
return self;
6359+
62716360
case 'H':
62726361
self.stats.request.head++;
6273-
new Subscribe(self, req, res, 0).end();
6362+
6363+
if (skipCors) {
6364+
new Subscribe(self, req, res, 0).end();
6365+
return self;
6366+
}
6367+
6368+
self._cors(req, res, function(req, res) {
6369+
new Subscribe(framework, req, res, 0).end();
6370+
});
6371+
62746372
return self;
6373+
62756374
case 'D':
62766375
self.stats.request['delete']++;
6277-
new Subscribe(self, req, res, 1).urlencoded();
6376+
6377+
if (skipCors) {
6378+
new Subscribe(self, req, res, 1).urlencoded();
6379+
return self;
6380+
}
6381+
6382+
self._cors(req, res, function(req, res) {
6383+
new Subscribe(framework, req, res, 1).urlencoded();
6384+
});
6385+
62786386
return self;
62796387
case 'P':
62806388
if (self._request_check_POST) {
62816389
if (multipart) {
62826390
self.stats.request.upload++;
6283-
new Subscribe(self, req, res, 2).multipart(multipart);
6391+
6392+
if (skipCors) {
6393+
new Subscribe(self, req, res, 2).multipart(multipart);
6394+
return self;
6395+
}
6396+
6397+
self._cors(req, res, function(req, res, multipart) {
6398+
new Subscribe(self, req, res, 2).multipart(multipart);
6399+
}, multipart);
6400+
6401+
return self;
6402+
62846403
} else {
62856404
if (method === 'PUT')
62866405
self.stats.request.put++;
6406+
else if (method === 'PATH')
6407+
self.stats.request.path++;
62876408
else
62886409
self.stats.request.post++;
6289-
new Subscribe(self, req, res, 1).urlencoded();
6410+
6411+
if (skipCors) {
6412+
new Subscribe(self, req, res, 1).urlencoded();
6413+
return self;
6414+
}
6415+
6416+
self._cors(req, res, function(req, res) {
6417+
new Subscribe(self, req, res, 1).urlencoded();
6418+
});
62906419
}
62916420
return self;
62926421
}
@@ -6301,6 +6430,142 @@ Framework.prototype._request_continue = function(req, res, headers, protocol) {
63016430
return self;
63026431
};
63036432

6433+
Framework.prototype._cors = function(req, res, fn, arg) {
6434+
6435+
var self = this;
6436+
var cors;
6437+
var is = false;
6438+
6439+
for (var i = 0; i < self._length_cors; i++) {
6440+
cors = self.routes.cors[i];
6441+
if (!framework_internal.routeCompare(req.path, cors.url, false, cors.isASTERIX))
6442+
continue;
6443+
is = true;
6444+
break;
6445+
}
6446+
6447+
if (!is) {
6448+
fn = null;
6449+
self.emit('request-end', req, res);
6450+
self._request_stats(false, false);
6451+
self.stats.request.blocked++;
6452+
res.writeHead(404);
6453+
res.end();
6454+
return;
6455+
}
6456+
6457+
is = false;
6458+
6459+
// compare
6460+
var isAllowed = false;
6461+
var headers = req.headers;
6462+
6463+
if (cors.headers) {
6464+
6465+
for (var i = 0, length = cors.headers.length; i < length; i++) {
6466+
if (headers[cors.headers[i]]) {
6467+
isAllowed = true;
6468+
break;
6469+
}
6470+
}
6471+
6472+
if (!isAllowed) {
6473+
fn = null;
6474+
self.emit('request-end', req, res);
6475+
self._request_stats(false, false);
6476+
self.stats.request.blocked++;
6477+
res.writeHead(404);
6478+
res.end();
6479+
return;
6480+
}
6481+
6482+
isAllowed = false;
6483+
}
6484+
6485+
if (cors.methods) {
6486+
6487+
var current = headers['access-control-request-method'] || req.method;
6488+
if (current !== 'OPTIONS') {
6489+
for (var i = 0, length = cors.methods.length; i < length; i++) {
6490+
if (current.indexOf(cors.methods[i]) !== -1)
6491+
isAllowed = true;
6492+
}
6493+
6494+
if (!isAllowed) {
6495+
fn = null;
6496+
self.emit('request-end', req, res);
6497+
self._request_stats(false, false);
6498+
self.stats.request.blocked++;
6499+
res.writeHead(404);
6500+
res.end();
6501+
return;
6502+
}
6503+
}
6504+
6505+
isAllowed = false;
6506+
}
6507+
6508+
var origin = headers['origin'].toLowerCase();
6509+
6510+
if (cors.origin) {
6511+
for (var i = 0, length = cors.origin.length; i < length; i++) {
6512+
if (cors.origin[i].indexOf(origin) !== -1) {
6513+
isAllowed = true;
6514+
break;
6515+
}
6516+
}
6517+
6518+
if (!isAllowed) {
6519+
fn = null;
6520+
self.emit('request-end', req, res);
6521+
self._request_stats(false, false);
6522+
self.stats.request.blocked++;
6523+
res.writeHead(404);
6524+
res.end();
6525+
return;
6526+
}
6527+
}
6528+
6529+
var tmp;
6530+
var name;
6531+
var isOPTIONS = req.method === 'OPTIONS';
6532+
6533+
res.setHeader('Access-Control-Allow-Origin', cors.origin ? cors.origin : '*');
6534+
6535+
if (cors.credentials)
6536+
res.setHeader('Access-Control-Allow-Credentials', 'true');
6537+
6538+
name = 'Access-Control-Allow-Methods';
6539+
6540+
if (cors.methods) {
6541+
res.setHeader(name, cors.methods.join(', '));
6542+
} else if (isOPTIONS) {
6543+
tmp = headers['access-control-request-method'];
6544+
if (tmp)
6545+
res.setHeader(name, tmp);
6546+
}
6547+
6548+
name = 'Access-Control-Allow-Headers';
6549+
6550+
if (cors.headers) {
6551+
res.setHeader(name, cors.headers.join(', '));
6552+
} else if (isOPTIONS) {
6553+
tmp = headers['access-control-request-headers'];
6554+
if (tmp)
6555+
res.setHeader(name, tmp);
6556+
}
6557+
6558+
if (!isOPTIONS)
6559+
return fn(req, res, arg);
6560+
6561+
fn = null;
6562+
self.emit('request-end', req, res);
6563+
self._request_stats(false, false);
6564+
res.writeHead(200);
6565+
res.end();
6566+
return self;
6567+
};
6568+
63046569
/**
63056570
* Upgrade HTTP (WebSocket)
63066571
* @param {HttpRequest} req
@@ -9921,7 +10186,6 @@ Controller.prototype.cors = function(allow, method, header, credentials) {
992110186

992210187
var self = this;
992310188
var origin = self.req.headers['origin'];
9924-
var isOPTIONS = self.req.method.toUpperCase() === 'OPTIONS';
992510189

992610190
if (origin === undefined)
992710191
return true;
@@ -10002,6 +10266,7 @@ Controller.prototype.cors = function(allow, method, header, credentials) {
1000210266
if (!isAllowed)
1000310267
return false;
1000410268

10269+
var isOPTIONS = self.req.method.toUpperCase() === 'OPTIONS';
1000510270
var tmp;
1000610271
var name;
1000710272

test/controllers/default.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,11 @@ exports.install = function() {
141141
// allow []
142142
// maximumSize
143143
framework.websocket('/', socket);
144-
145144
framework.route('/theme-green/', view_theme);
145+
framework.cors('/api/*', '*');
146+
framework.cors('/cors/origin-all/', '*');
147+
framework.cors('/cors/origin-not/', ['http://www.petersirka.eu', 'http://www.858project.com']);
148+
framework.cors('/cors/headers/', '*', 'post,put,delete,options', true);
146149
};
147150

148151
function plain_options() {

0 commit comments

Comments
 (0)