forked from harald-lang/datalog
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.js
150 lines (125 loc) · 4.37 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Backend for the Datalog web interface.
*/
var express = require('express'),
util = require('util'),
spawn = require('child_process').spawn,
fs = require('fs'),
expressValidator = require('express-validator'),
bodyParser = require('body-parser');
// read configuration file
var config = JSON.parse(fs.readFileSync('config.json','utf8'));
var app = express();
// serve static content from the 'static' directory
app.use(express.static('public'));
// use body parser to get access to form data
app.use(bodyParser.urlencoded({ extended: true }));
// use the input validator
app.use(expressValidator());
app.use("/bower", express.static('bower_components'));
// add a 'endsWith' function to String
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
// used to create temporary files
var counter = 0;
// Datalog REST service
app.post('/datalog', function (req, res) {
// log all queries
var logFile = config.tempDirectory + '/log-' + counter + '.dl';
fs.writeFile(logFile, req.body.ruleset + "\n" + req.body.query, function(err) {
if(err) {
console.log(err);
}
});
// validate user input
// WARNING: Don't allow forward slashes!
req.assert('ruleset', 'required').notEmpty();
req.assert('ruleset', 'max. 6000 characters allowed').len(0, 6000);
req.assert('ruleset', 'ruleset contains unallowed characters').matches(/^([_=:'\-\+<>\s\w\d\n\(\)%,;\.]|\\=)+$/);
req.assert('query', 'required').notEmpty();
req.assert('query', 'max. 1024 characters allowed').len(0, 1024);
req.assert('query', 'query contains unallowed characters').matches(/^[_='\-\+\w\d\s(),\.]+$/);
var validationErrors = req.validationErrors();
if (validationErrors) {
res.json({
'answer': answer,
'error': 'There have been validation errors: ' + util.inspect(validationErrors) });
return;
}
// write the ruleset to a temporary file
counter++;
var tempFile = config.tempDirectory + '/ruleset-' + counter + '.dl';
fs.writeFile(tempFile, req.body.ruleset, function(err) {
if(err) {
console.log(err);
}
});
// start a DES process and connect the streams
var desProcess = spawn(config.desExecutable,[],{cwd:config.desDirectory});
var desProcessIsRunning = true;
var computationTimeExceeded = false;
var answer = '';
// terminates the process if the computation takes too much time
setTimeout(function() {
if (desProcessIsRunning) {
computationTimeExceeded = true;
console.log("Terminating DES due to timeout.");
desProcess.kill('SIGKILL');
try {
fs.unlinkSync(tempFile);
} catch (err) {}
}
}, config.desTimeLimitMillis);
// reading the standard output of DES
desProcess.stdout.on('data', function (data) {
data = '' + data;
answer = answer + data;
});
// send the answer after the computation has been finished
desProcess.on('exit', function (code) {
desProcessIsRunning = false;
try {
fs.unlinkSync(tempFile);
} catch (err) {}
if (computationTimeExceeded) {
answer = "--- Sorry, computation time exceeded... ----"
}
answer=answer.replace(/^[^]*\{/,'{');
answer=answer.replace(/}[^]*$/,'}');
res.json({
'answer': answer
});
});
// consult the previously written file
desProcess.stdin.write('/consult ' + tempFile + '\n');
// transform query into a single line string
var query = req.body.query.replace(/\n/,' ');
console.log("Executing query " + counter + ": " + query);
// submit query
desProcess.stdin.write(query + '\n');
// terminate the process
desProcess.stdin.end();
});
// Examples REST service. Delivers the examples to the client.
app.get('/examples', function (req, res) {
// reads the contents of the examples directory
// and sends a list to the client.
// the examples are delivered as static content
var exampleDescriptors = [];
var exampleFiles = fs.readdirSync(config.examplesDirectory);
exampleFiles.forEach(function(file) {
if (file.endsWith(".json")) {
var fileContents = fs.readFileSync(config.examplesDirectory + '/' + file,'utf8');
var example = JSON.parse(fileContents);
exampleDescriptors.push({
"name": example.name,
"description": example.description,
"source": example.source,
"url": "/examples" + "/" + file,
});
}
});
res.json(exampleDescriptors);
});
app.listen(config.port);