slower 1.1.12 → 1.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/router.js +22 -28
- package/lib/utils.js +6 -4
- package/package.json +1 -1
- package/readme.md +114 -1
package/lib/router.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const { noop, slugify, isSparseEqual, last, renderDynamicHTML, setSocketLocals, setSocketSecurityHeaders } = require('./utils');
|
|
3
|
+
const { clone, noop, slugify, isSparseEqual, last, renderDynamicHTML, setSocketLocals, setSocketSecurityHeaders } = require('./utils');
|
|
4
4
|
|
|
5
5
|
class Route {
|
|
6
6
|
constructor (path, type, callback) {
|
|
@@ -37,7 +37,7 @@ class SlowerRouter {
|
|
|
37
37
|
this.routes = [];
|
|
38
38
|
this.middleware = [noop];
|
|
39
39
|
this.fallback = noop;
|
|
40
|
-
this.allowedMethods =
|
|
40
|
+
this.allowedMethods = clone(SlowerRouter.http_methods);
|
|
41
41
|
this.blockedMethodCallback = noop;
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -72,7 +72,7 @@ class SlowerRouter {
|
|
|
72
72
|
setRoute = function (path = '/', type = 'GET', callback) {
|
|
73
73
|
let stat = new Route(path, type, callback);
|
|
74
74
|
this.routes.push(stat);
|
|
75
|
-
return
|
|
75
|
+
return this;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -82,13 +82,10 @@ class SlowerRouter {
|
|
|
82
82
|
* @returns {Array} The used middlewares list
|
|
83
83
|
*/
|
|
84
84
|
setMiddleware = function (callback) {
|
|
85
|
-
|
|
85
|
+
this.middleware.push((typeof callback == 'function' ? callback : noop));
|
|
86
|
+
return this;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
89
|
-
// Use app.setStatic('/*', __dirname+'/') for serving all local files.
|
|
90
|
-
// or use app.setStatic('/*', __dirname+'/public/') for serving files in a specific folder (public).
|
|
91
|
-
// This is a one-liner for the setRoute function, when the route responds with a simple page or document
|
|
92
89
|
/**
|
|
93
90
|
* Defines a route for a determined request method
|
|
94
91
|
* @category Router
|
|
@@ -97,8 +94,6 @@ class SlowerRouter {
|
|
|
97
94
|
* @param {String} mime The file's mime type
|
|
98
95
|
* @param {Object} replacementData The replacement data map for the dynamic HTML rendering
|
|
99
96
|
* @returns {Object} An Route object already configured
|
|
100
|
-
* @example <caption> Defining a simple GET route:</caption>
|
|
101
|
-
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
102
97
|
*/
|
|
103
98
|
/**
|
|
104
99
|
* Dynamic rendering example:
|
|
@@ -117,8 +112,9 @@ class SlowerRouter {
|
|
|
117
112
|
* age: 29
|
|
118
113
|
* }));
|
|
119
114
|
*/
|
|
120
|
-
setDynamic = function (path, file = '', mime = '', replacementData) {
|
|
121
|
-
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
115
|
+
setDynamic = function (path, file = '', mime = '', replacementData = null) {
|
|
116
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8');
|
|
117
|
+
if (file.includes('{%}')) { file = file.replace(/\{\%\}/gim, path.replace(/\//gim,'')); }
|
|
122
118
|
let stat = new Route(path, 'GET', (req, res) => {
|
|
123
119
|
let data, targetFile, extension, targetMime, targetEncoding;
|
|
124
120
|
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
@@ -143,7 +139,7 @@ class SlowerRouter {
|
|
|
143
139
|
res.end();
|
|
144
140
|
});
|
|
145
141
|
this.routes.push(stat);
|
|
146
|
-
return
|
|
142
|
+
return this;
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
@@ -156,13 +152,13 @@ class SlowerRouter {
|
|
|
156
152
|
* @param {String} path The route that will be defined
|
|
157
153
|
* @param {String} file The file path that will be used to respond to the route
|
|
158
154
|
* @param {String} mime The file's mime type
|
|
159
|
-
* @param {String} encoding The file's encoding (defaults to UTF-8)
|
|
160
155
|
* @returns {Object} An Route object already configured
|
|
161
156
|
* @example <caption> Defining a simple GET route:</caption>
|
|
162
|
-
* setStatic('/login', __dirname+'/public/static/views/
|
|
157
|
+
* setStatic('/login', __dirname+'/public/static/views/{%}.html', 'text/html');
|
|
163
158
|
*/
|
|
164
159
|
setStatic = function (path, file = '', mime = '') {
|
|
165
|
-
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
160
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8');
|
|
161
|
+
if (file.includes('{%}')) { file = file.replace(/\{\%\}/gim, path.replace(/\//gim,'')); }
|
|
166
162
|
let stat = new Route(path, 'GET', (req, res) => {
|
|
167
163
|
let data, targetFile, extension, targetMime, targetEncoding;
|
|
168
164
|
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
@@ -184,7 +180,7 @@ class SlowerRouter {
|
|
|
184
180
|
res.end();
|
|
185
181
|
});
|
|
186
182
|
this.routes.push(stat);
|
|
187
|
-
return
|
|
183
|
+
return this;
|
|
188
184
|
}
|
|
189
185
|
|
|
190
186
|
/**
|
|
@@ -192,10 +188,8 @@ class SlowerRouter {
|
|
|
192
188
|
* @category Router
|
|
193
189
|
* @param {String} callback The function to execute for unhandled routes
|
|
194
190
|
* @returns {undefined}
|
|
195
|
-
* @example <caption> Defining a simple GET route:</caption>
|
|
196
|
-
* setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html', 'utf8');
|
|
197
191
|
*/
|
|
198
|
-
setFallback = function (callback) { this.fallback = callback; }
|
|
192
|
+
setFallback = function (callback) { this.fallback = callback; return this; }
|
|
199
193
|
|
|
200
194
|
// SERVES FILES FOR SPECIFIC PATHS. WILDCARS ALLOWED. BE CAREFUL - PATH TRAVERSAL MAY BE POSSIBLE
|
|
201
195
|
// Use app.setStatic('/*', __dirname+'/') for serving all local files.
|
|
@@ -229,9 +223,9 @@ class SlowerRouter {
|
|
|
229
223
|
* age: 29
|
|
230
224
|
* }));
|
|
231
225
|
*/
|
|
232
|
-
setFallbackFile = function (file = '', mime = '', replacementData) {
|
|
226
|
+
setFallbackFile = function (file = '', mime = '', replacementData = null) {
|
|
233
227
|
this.fallback = function fb (req,res) {
|
|
234
|
-
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8')
|
|
228
|
+
let encoding = (mime === SlowerRouter.mime_table['default'] ? undefined : 'utf-8');
|
|
235
229
|
let data, targetFile, extension, targetMime, targetEncoding;
|
|
236
230
|
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
237
231
|
targetFile = file.replace(/\//gim, '\\');
|
|
@@ -254,6 +248,7 @@ class SlowerRouter {
|
|
|
254
248
|
res.write(data);
|
|
255
249
|
res.end();
|
|
256
250
|
}
|
|
251
|
+
return this;
|
|
257
252
|
}
|
|
258
253
|
|
|
259
254
|
// Sets the methods that the application will respond to. The rest is simply discarded with empty responses.
|
|
@@ -282,7 +277,7 @@ class SlowerRouter {
|
|
|
282
277
|
res.writeHead(405); // sends the meme error 418 'i am a teapot'
|
|
283
278
|
res.end();
|
|
284
279
|
};
|
|
285
|
-
return this
|
|
280
|
+
return this;
|
|
286
281
|
}
|
|
287
282
|
|
|
288
283
|
// Starts the server - listening at <host>:<port>
|
|
@@ -290,10 +285,8 @@ class SlowerRouter {
|
|
|
290
285
|
* @category Router
|
|
291
286
|
* @param {Number} port The port number the server will listen to.
|
|
292
287
|
* @param {String} host The host's network interface address the server will listen into (use a falsy value or '0.0.0.0' for listening on all).
|
|
293
|
-
* @param {Function}
|
|
294
|
-
* @returns {Object} The
|
|
295
|
-
* @example <caption> Allowing only GET and POST:</caption>
|
|
296
|
-
* app.setAllowedMethods(['GET', 'POST']);
|
|
288
|
+
* @param {Function} callback The function to execute after starting the server.
|
|
289
|
+
* @returns {Object<http.Server>} The server instance
|
|
297
290
|
*/
|
|
298
291
|
start = function (port = 8080, host = undefined, callback = () => {}) {
|
|
299
292
|
let routes = this.routes;
|
|
@@ -339,7 +332,8 @@ class SlowerRouter {
|
|
|
339
332
|
});
|
|
340
333
|
callback(server);
|
|
341
334
|
if (!host) host = undefined; // Turn falsy values into undefined, for default behaviour
|
|
342
|
-
|
|
335
|
+
server.listen(port, host);
|
|
336
|
+
return this;
|
|
343
337
|
}
|
|
344
338
|
}
|
|
345
339
|
|
package/lib/utils.js
CHANGED
|
@@ -44,7 +44,7 @@ const isSparseEqual = (str1 = '', str2 = '') => {
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* It's a template engine, to render HTML containing template spaces.
|
|
47
|
-
* The charset for replacement is
|
|
47
|
+
* The charset for replacement is {{content}}
|
|
48
48
|
* @since 1.2.5
|
|
49
49
|
*
|
|
50
50
|
* @param {String} html The HTML code
|
|
@@ -52,7 +52,7 @@ const isSparseEqual = (str1 = '', str2 = '') => {
|
|
|
52
52
|
* @return {String} The HTML with the templates replaces
|
|
53
53
|
*
|
|
54
54
|
* @example <caption> Rendering: </caption>
|
|
55
|
-
* var template = 'Hello, my name is
|
|
55
|
+
* var template = 'Hello, my name is {{name}}. I\\'m {{age}} years old.';
|
|
56
56
|
* console.log(TemplateEngine(template, {
|
|
57
57
|
* name: "Krasimir",
|
|
58
58
|
* age: 29
|
|
@@ -62,7 +62,7 @@ const renderDynamicHTML = (html, patterns) => {
|
|
|
62
62
|
let template = html;
|
|
63
63
|
for (let item in patterns) {
|
|
64
64
|
template = html.replace(
|
|
65
|
-
new RegExp('
|
|
65
|
+
new RegExp('{{'+item+'}}', 'gim'),
|
|
66
66
|
patterns[item]
|
|
67
67
|
);
|
|
68
68
|
}
|
|
@@ -141,4 +141,6 @@ const setSocketSecurityHeaders = (req) => {
|
|
|
141
141
|
|
|
142
142
|
const toBool = [() => true, () => false];
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
const clone = (object) => JSON.parse(JSON.stringify(object));
|
|
145
|
+
|
|
146
|
+
module.exports = { clone, noop, slugify, isSparseEqual, toBool, last, renderDynamicHTML, setSocketLocals, setSocketSecurityHeaders };
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -3,6 +3,116 @@
|
|
|
3
3
|
Slower is a small web framework, express-like, but simpler and limited.
|
|
4
4
|
It allows for generic route-declaration, fallback pages, and multiple middleware functions.
|
|
5
5
|
|
|
6
|
+
### API Methods:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
app.enableStrictHeaders(): this
|
|
10
|
+
|
|
11
|
+
> Enables the use of the set of 'Strict Headers'.
|
|
12
|
+
> These headers increase security levels, and are a good practice to apply.
|
|
13
|
+
> However, using these headers in testing scenarios are not a need,
|
|
14
|
+
and may have buggy or negative effects. So, apply those to simulate scenarios only.
|
|
15
|
+
> Headers configured:
|
|
16
|
+
> Content-Security-Policy
|
|
17
|
+
> Cross-Origin-Opener-Policy
|
|
18
|
+
> Cross-Origin-Resource-Policy
|
|
19
|
+
> Origin-Agent-Cluster
|
|
20
|
+
> Referrer-Policy
|
|
21
|
+
> X-DNS-Prefetch-Control (disabled)
|
|
22
|
+
> X-Download-Options
|
|
23
|
+
> X-Frame-Options
|
|
24
|
+
> X-XSS-Protection (disabled)
|
|
25
|
+
> X-Powered-By (removed)
|
|
26
|
+
> Returns the own object instance, so that methods can be chained.
|
|
27
|
+
```
|
|
28
|
+
```
|
|
29
|
+
app.disableStrictHeaders(): this
|
|
30
|
+
|
|
31
|
+
> Disables the use of the set of 'Strict Headers'.
|
|
32
|
+
> See 'enableStrictHeaders()' for more information.
|
|
33
|
+
> Returns the own object instance, so that methods can be chained.
|
|
34
|
+
```
|
|
35
|
+
```
|
|
36
|
+
app.setRoute(string: path = '/', string: type = 'GET', function: callback): this
|
|
37
|
+
|
|
38
|
+
> Creates a new route for path defined in 'path', responding to the HTTP verb defined in 'type' argument.
|
|
39
|
+
> The callback is executed when the route is accessed.
|
|
40
|
+
> Returns the own object instance, so that methods can be chained.
|
|
41
|
+
```
|
|
42
|
+
```
|
|
43
|
+
app.setMiddleware(function: callback): this
|
|
44
|
+
|
|
45
|
+
> Sets a new middleware function: callback function will be accessed for every server access.
|
|
46
|
+
> Many middlewares can be defined, and will be applied in the order they are defined.
|
|
47
|
+
> Returns the own object instance, so that methods can be chained.
|
|
48
|
+
```
|
|
49
|
+
```
|
|
50
|
+
app.setDynamic(string: path, string: file = '', string: mime = '', object: replacementData = null): this
|
|
51
|
+
|
|
52
|
+
> Creates a new GET route for path defined in 'path'.
|
|
53
|
+
> This is a custom file-response route, configured for template rendering just before response.
|
|
54
|
+
> Providing an object as 'replacementData' in this format { valueToBeReplaced: valueToReplace },
|
|
55
|
+
allows for template rendering. The value to replace in the file, uses this notation: '<{content}>'.
|
|
56
|
+
> URL reference in filename:
|
|
57
|
+
> For direct references, it is possible to use the token '{%}' to replace the filename for the URL.
|
|
58
|
+
> Ex:
|
|
59
|
+
app.setStatic('/login', './templates/{%}.html', 'text/html');
|
|
60
|
+
This will access the 'login.html' file when the route '/login' is accessed.
|
|
61
|
+
> Example:
|
|
62
|
+
Responding a route for '/custom' with file 'custom.html':
|
|
63
|
+
app.setDynamic('/custom', './templates/custom.html', 'text/html', { smile: ':)' })
|
|
64
|
+
In file './templates/custom.html':
|
|
65
|
+
"<h2> This is a custom thing: <{smile}> </h2>"
|
|
66
|
+
Rendered in browser:
|
|
67
|
+
<h2> This is a custom thing: :) </h2>
|
|
68
|
+
> Returns the own object instance, so that methods can be chained.
|
|
69
|
+
```
|
|
70
|
+
```
|
|
71
|
+
app.setStatic(string: path, string: file = '', string: mime = ''): this
|
|
72
|
+
|
|
73
|
+
> Creates a new GET route for path defined in 'path', responding with the specified file and MIME type.
|
|
74
|
+
> URL reference in filename:
|
|
75
|
+
> For direct references, it is possible to use the token '{%}' to replace the filename for the URL.
|
|
76
|
+
> Ex:
|
|
77
|
+
app.setStatic('/login', './templates/{%}.html', 'text/html');
|
|
78
|
+
This will access the 'login.html' file when the route '/login' is accessed.
|
|
79
|
+
> Example: A route for '/login' page, responding with 'login.html' file
|
|
80
|
+
setStatic('/login', __dirname+'/public/static/views/login.html', 'text/html');
|
|
81
|
+
> Returns the own object instance, so that methods can be chained.
|
|
82
|
+
```
|
|
83
|
+
```
|
|
84
|
+
app.setFallback(function: callback): this
|
|
85
|
+
|
|
86
|
+
> Creates a function for fallback state. When no other routes intercept the route, this will be used.
|
|
87
|
+
> Special use for 'page not found' fallback pages or highly customized routes and situations.
|
|
88
|
+
> Returns the own object instance, so that methods can be chained.
|
|
89
|
+
```
|
|
90
|
+
```
|
|
91
|
+
app.setFallbackFile (string: file = '', string: mime = '', object: replacementData = null): this
|
|
92
|
+
|
|
93
|
+
> Creates a function for fallback state. When no other routes intercept the route, this will be used.
|
|
94
|
+
> Equivalent to setFallback, but responds with a file. Allows for template rendering.
|
|
95
|
+
> See 'setDynamic' for more information about template rendering.
|
|
96
|
+
> Special use for 'page not found' fallback pages, ex: './e404.html'.
|
|
97
|
+
> Returns the own object instance, so that methods can be chained.
|
|
98
|
+
```
|
|
99
|
+
```
|
|
100
|
+
app.setAllowedMethods(array: methods = []): this
|
|
101
|
+
|
|
102
|
+
> Sets a list of methods to respond to.
|
|
103
|
+
> By using this, it is possible to restrict the application to avoid
|
|
104
|
+
responding to dangerous HTTP verbs, such as 'DELETE'.
|
|
105
|
+
> By default, all methods are allowed (see Slower.constructor.http_methods)
|
|
106
|
+
> Calling this function without parameters is an easy way to block responses to all requests (lock server).
|
|
107
|
+
> Returns the own object instance, so that methods can be chained.
|
|
108
|
+
```
|
|
109
|
+
```
|
|
110
|
+
app.start(number|string: port = 8080, string: host = undefined, function: callback = ()=>{}): this
|
|
111
|
+
|
|
112
|
+
> Starts the server, at a specific host and port, then calling the callback function.
|
|
113
|
+
> Not defining a specific port or host will start the server at '0.0.0.0:8080'.
|
|
114
|
+
> Returns the own object instance, so that methods can be chained.
|
|
115
|
+
```
|
|
6
116
|
|
|
7
117
|
Example usage:
|
|
8
118
|
```
|
|
@@ -16,7 +126,7 @@ app.setMiddleware((req, res) => {
|
|
|
16
126
|
console.log(`${req.time} - ${req.method} : ${req.url}`);
|
|
17
127
|
});
|
|
18
128
|
|
|
19
|
-
app.setStatic('/favicon.ico', __dirname+'/public/
|
|
129
|
+
app.setStatic('/favicon.ico', __dirname+'/public/{%}');
|
|
20
130
|
|
|
21
131
|
app.setRoute('/', 'GET', (req, res) => {
|
|
22
132
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
@@ -46,6 +156,7 @@ app.start(port, null, () => {
|
|
|
46
156
|
console.log(`Running on localhost:${port}`);
|
|
47
157
|
console.log(app);
|
|
48
158
|
});
|
|
159
|
+
|
|
49
160
|
```
|
|
50
161
|
### API modifications on 'net.Socket' instances:
|
|
51
162
|
- The API modifies every ```net.Socket``` instance BEFORE it is passed
|
|
@@ -71,6 +182,7 @@ So, considering the common callback of ```(req, res)```, the session container w
|
|
|
71
182
|
methods do exacly that. The strict headers are disabled by default, as some resources are too strict,
|
|
72
183
|
but it is also possible to enable them all, and then set a middleware to override any header.
|
|
73
184
|
- Headers set by 'enableStrictHeaders':
|
|
185
|
+
```
|
|
74
186
|
Content-Security-Policy: default-src=none; script-src=self; connect-src=self; img-src=self;
|
|
75
187
|
style-src=self; frame-ancestors=none; form-action=self;
|
|
76
188
|
Cross-Origin-Opener-Policy: same-origin
|
|
@@ -84,3 +196,4 @@ So, considering the common callback of ```(req, res)```, the session container w
|
|
|
84
196
|
X-Frame-Options: DENY
|
|
85
197
|
X-Powered-By: (This header is removed if a response includes it)
|
|
86
198
|
X-XSS-Protection: 0
|
|
199
|
+
```
|