Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion builders.js
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,7 @@ ActionCaller.prototype.exec = function() {
let $ = self.$;

$.$end = false;
$.$cached = false;
$.name = action.name;
$.id = action.id;
$.error = self.error;
Expand Down Expand Up @@ -1465,7 +1466,7 @@ ActionCaller.prototype.exec = function() {
$.$timeout = null;
}

if (!err) {
if (!err && !$.$cached) {
if (action.jsoutput)
response = action.jsoutput.transform(response).response || {};

Expand All @@ -1476,6 +1477,10 @@ ActionCaller.prototype.exec = function() {
$.payload[action.extend] = response;
response = $.payload;
}

// The "cache" option of the action: the final response is kept for the next call with the same key
if (action.cache && response !== undefined)
action.cache($, response);
}

if (err) {
Expand Down Expand Up @@ -1564,6 +1569,16 @@ ActionCaller.prototype.exec = function() {
if (self.options.progress)
$.progress2 = self.options.progress;

// The "cache" option of the action: a cached response skips the middleware and the action
if (action.cache) {
let cached = action.cache($);
if (cached !== undefined) {
$.$cached = true;
$.$callback(null, cached && typeof(cached) === 'object' ? CLONE(cached) : cached);
return;
}
}

if (action.timeout) {
$.$timeout = setTimeout(function() {
$.invalid('Timeout.');
Expand Down
15 changes: 15 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
- fixed `AIPARSER()` with tool calling
- added a new controller method `$.content(value, content_type)`
- added a new event `reset` in the FlowStream
- fixed `CRON()` with the step form (`*/15`, `5/20`): it matches the values of the step and not every value
- fixed the `limit` argument of `setTimeout2()`
- fixed `AJAX()` for the answers with `application/xml` and `text/xml`
- fixed `F.python()` without a callback (the promise resolves with the output) and `F.pipinstall()` without a callback
- fixed `LOGMAIL()` with an object as the body
- fixed `IMPORT()`: an answer with the status 400 or more, or a file that is not a module, gives an error
- fixed the tunnel client (`proxy.js`): the types of the messages "error" and "abort"
- fixed the controller method `$.href()`
- fixed `QueryBuilder.gridfields()`: the accepted fields are applied
- fixed the handling of the pending requests (a single pass instead of `splice()` for each finished request)
- fixed `LDAP()`: a bind with a status other than 0 gives an error even when the server sends no text
- added support for the `cache` option of `NEWACTION()` (the response is kept for the same `query`, `params`, `input`, `user` or `language`)
- added the compact schema types `json` and `ip` (the values are validated)
- fixed `attachmentfs()` in the mail: the file of the storage is read as a Buffer and the message is completed (it threw a `TypeError` and the SMTP connection stayed open)
- fixed `attachment()` with a `Buffer` in the mail: the Base64 of the whole Buffer is split into lines (the bytes were split into pieces of 68 bytes and each one got its own padding)

========================
0.0.19
Expand Down
4 changes: 2 additions & 2 deletions controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1081,8 +1081,8 @@ Controller.prototype.$route = function() {

};

Controller.prototype.href = function(key, value) {
return F.TViewEngine.prototype.href.call(this, key, value);
Controller.prototype.href = function() {
return F.TViewEngine.View.prototype.href.apply(this, arguments);
};

function readfile(filename, callback) {
Expand Down
8 changes: 7 additions & 1 deletion cron.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
const Days = ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa'];

// The method compares date with parsed cron data
// The first value of each field: minute, hour, day of the month, month, day of the week
const CRON_MIN = [0, 0, 1, 1, 0];

function cronexec(output, date) {

var m = date.getMinutes();
Expand All @@ -28,7 +31,10 @@ function cronexec(output, date) {
break;
}
} else if (m.type === 'every') {
if (m.value[i] !== '*' && m.value[i] % val !== 0) {
// m.value = [start, step] where the start is "*" or a number: "*/15" or "5/15"
var step = m.value[1];
var start = m.value[0] === '*' ? CRON_MIN[i] : m.value[0];
if (!step || val < start || (val - start) % step !== 0) {
is = false;
break;
}
Expand Down
4 changes: 2 additions & 2 deletions global.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ global.AJAX = function(url, data, callback) {
switch (type.toLowerCase()) {
case 'text/xml':
case 'application/xml':
value = response.body ? response.body.parseXML(self.$replace ? true : false) : {};
value = response.body ? DEF.parsers.xml(response.body) : {};
break;
case 'application/x-www-form-urlencoded':
value = response.body ? DEF.parsers.urlencoded(response.body) : {};
Expand Down Expand Up @@ -305,7 +305,7 @@ global.setTimeout2 = function(id, callback, timeout, limit, a, b, c, d, e) {
clearTimeout(cache.timer);
cache.count++;
} else
cache = internal[key] = {};
cache = internal[key] = { count: 0 };

cache.callback = callback;
cache.timer = setTimeout(timeout2, timeout, key, a, b, c, d, e);
Expand Down
65 changes: 41 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1017,10 +1017,22 @@ F.import = function(url, callback) {

F.download(url, filename, function(err, response) {
var m;

// An error page is not a module
if (!err && response && response.status >= 400) {
err = new Error('The address answered with the status ' + response.status);
F.Fs.unlink(filename, NOOP);
}

if (!err) {
m = require(filename);
F.temporary.dependencies[url] = 1;
try {
m = require(filename);
F.temporary.dependencies[url] = 1;
} catch (e) {
err = e;
}
}

callback && callback(err, m, response);
});
};
Expand Down Expand Up @@ -1087,14 +1099,14 @@ F.cleanup = function(stream, callback) {

F.python = function(filename, callback) {
if (!callback)
return new Promise((resolve, reject) => F.python(filename, (err, response) => err ? reject(err) : response));
return new Promise((resolve, reject) => F.python(filename, (err, response) => err ? reject(err) : resolve(response)));
F.Child.exec(F.config.$python + ' ' + filename, { cwd: F.Path.dirname(filename) }, callback);
};

F.pipinstall = function(name, callback) {

if (!callback)
return new Promise((resolve, reject) => F.npminstall(name, err => err ? reject(err) : resolve()));
return new Promise((resolve, reject) => F.pipinstall(name, err => err ? reject(err) : resolve()));

var args = {};
args.cwd = F.directory;
Expand Down Expand Up @@ -1255,31 +1267,36 @@ F.loadservices = function() {
return;
}

let index = 0;
// One pass: the requests that are still alive are moved to the front of the same array (splice() moves the rest of the array each time)
let pending = F.temporary.pending;
let count = 0;

while (true) {
let ctrl = F.temporary.pending[index];
if (ctrl) {
if (ctrl.destroyed || ctrl.res.headersSent || ctrl.res.writableEnded) {
F.temporary.pending.splice(index, 1);
} else if (ctrl.timeout <= 0) {
for (let index = 0; index < pending.length; index++) {

F.stats.response.timeout++;
let ctrl = pending[index];

if (F.timeouts.push((NOW = new Date()).toJSON() + ' ' + ctrl.url) > 5)
F.timeouts.shift();
if (!ctrl)
continue;

ctrl.fallback(408);
F.temporary.pending.splice(index, 1);
} else {
F.stats.request.pending++;
ctrl.timeout -= 5; // 5 seconds
index++;
}
} else
break;
if (ctrl.destroyed || ctrl.res.headersSent || ctrl.res.writableEnded)
continue;

if (ctrl.timeout <= 0) {

F.stats.response.timeout++;

if (F.timeouts.push((NOW = new Date()).toJSON() + ' ' + ctrl.url) > 5)
F.timeouts.shift();

ctrl.fallback(408);
continue;
}

ctrl.timeout -= 5; // 5 seconds
pending[count++] = ctrl;
}

pending.length = count;
F.stats.request.pending = F.temporary.pending.length;

}, 5000);
Expand Down Expand Up @@ -2737,7 +2754,7 @@ F.logmail = function(email, subject, body, callback) {
if (!subject)
subject = F.config.name;

var body = '<!DOCTYPE html><html><head><title>' + subject + '</title><meta charset="utf-8" /></head><body><pre style="max-width:600px;font-size:13px;line-height:16px;white-space:pre-line">' + (typeof(body) === 'object' ? JSON.stringify(body).escape() : body) + '</pre></body></html>';
var body = '<!DOCTYPE html><html><head><title>' + subject + '</title><meta charset="utf-8" /></head><body><pre style="max-width:600px;font-size:13px;line-height:16px;white-space:pre-line">' + (typeof(body) === 'object' ? JSON.stringify(body).encode() : body) + '</pre></body></html>';
return F.def.onMail(email, subject, body, callback);
};

Expand Down
5 changes: 3 additions & 2 deletions ldap.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,9 @@ Reader.prototype.parse = function() {
obj.dn = self.readstring();
obj.error = self.readstring();

if (obj.error) {
self.callback(obj.error, null);
// A bind that was not successful (the status is not 0) ends here, also when the server sends no diagnostic text (OpenLDAP: 49)
if (obj.error || obj.status) {
self.callback(obj.error || ('LDAP bind error ' + obj.status), null);
return;
}

Expand Down
26 changes: 9 additions & 17 deletions mail.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Message.prototype.attachmentfs = function(storage, id, name, contentid) {

if (name) {
ext = F.TUtils.getExtension(name);
type = F.TUtils.contentTypes[ext];
type = F.TUtils.contentTypes[ext] || 'application/octet-stream';
}

var obj = {};
Expand Down Expand Up @@ -328,19 +328,19 @@ Mailer.$writeattachment = function(obj) {
var stream;

if (attachment.storage) {
FILESTORAGE(attachment.storage).readbase64(attachment.filename, function(err, meta) {
FILESTORAGE(attachment.storage).readbuffer(attachment.filename, function(err, buffer, meta) {
if (err) {
F.error(err, 'Mail.filestorage()', attachment.filename);
Mailer.$writeattachment(obj);
} else {

if (!attachment.name) {
attachment.name = meta.name;
attachment.type = meta.type;
attachment.type = meta.type || 'application/octet-stream';
attachment.extension = meta.ext;
}

writeattachemnt_stream(attachment, obj, meta.stream);
writeattachemnt_stream(attachment, obj, buffer);
}
});
} else {
Expand Down Expand Up @@ -413,20 +413,12 @@ function writeattachmentbytes(chunk) {

function writeattachmentbytesinline(mailerdata, chunk) {

var length = chunk.length;
var count = 0;
var beg = 0;

while (count < length) {

count += 68;

if (count > length)
count = length;
// The Base64 of the whole Buffer is split into lines (splitting the bytes would add padding to each line)
var value = chunk.toString('base64');
var length = value.length;

Mailer.$writeline(mailerdata, chunk.slice(beg, count).toString('base64'));
beg = count;
}
for (var index = 0; index < length; index += 68)
Mailer.$writeline(mailerdata, value.substring(index, index + 68));
}

Mailer.try = function(options, callback) {
Expand Down
4 changes: 2 additions & 2 deletions proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
BUFFER_END.writeUInt8(2);

const BUFFER_ERROR = Buffer.alloc(1);
BUFFER_END.writeUInt8(3);
BUFFER_ERROR.writeUInt8(3);

const BUFFER_ABORT = Buffer.alloc(1);
BUFFER_END.writeUInt8(4);
BUFFER_ABORT.writeUInt8(4);

const REG_ANSI = /\x1B\[[0-?]*[ -/]*[@-~]/g;
const Pending = {};
Expand Down
2 changes: 1 addition & 1 deletion querybuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ QBP.gridfields = function(fields, allowed) {
}
}

if (!count)
if (count)
t.options.fields = newfields;

return t;
Expand Down
Loading
Loading