Skip to content

Commit 151b81e

Browse files
Merge pull request #446 from ansble/feature/refactor-some-complex-functions
Refactors two of the complex functions to be a little simpler
2 parents 9e705c9 + 9fa0d78 commit 151b81e

File tree

7 files changed

+153
-133
lines changed

7 files changed

+153
-133
lines changed

routes/handleStaticFile.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
'use strict';
2+
3+
const fs = require('fs')
4+
, events = require('harken')
5+
, zlib = require('zlib')
6+
, brotli = require('iltorb')
7+
, mime = require('mime')
8+
9+
, getCompression = require('../utils').getCompression
10+
11+
, not = require('../utils').not
12+
, unmodifiedStatus = 304
13+
, succesStatus = 200
14+
15+
, compressionExtension = (type) => {
16+
if (type === 'br') {
17+
return 'brot';
18+
} else if (type === 'deflate') {
19+
return 'def';
20+
} else {
21+
return 'tgz';
22+
}
23+
}
24+
25+
, getCompressor = (type) => {
26+
if (type === 'br') {
27+
return brotli.compressStream();
28+
} else if (type === 'deflate') {
29+
return zlib.createDeflate();
30+
} else {
31+
return zlib.createGzip();
32+
}
33+
}
34+
35+
, streamFile = (compression, file, connection) => {
36+
const extension = compressionExtension(compression)
37+
, compressor = getCompressor(compression);
38+
39+
fs.stat(`${file}.${extension}`, (err, exists) => {
40+
if (!err && exists.isFile()) {
41+
fs.createReadStream(`${file}.${extension}`).pipe(connection.res);
42+
} else {
43+
// no compressed file yet...
44+
fs.createReadStream(file).pipe(compressor)
45+
.pipe(connection.res);
46+
47+
fs.createReadStream(file).pipe(compressor)
48+
.pipe(fs.createWriteStream(`${file}.${extension}`));
49+
}
50+
});
51+
};
52+
53+
module.exports = (file, connection, config) => {
54+
const req = connection.req
55+
, res = connection.res
56+
, pathname = connection.path.pathname
57+
, compression = getCompression(req.headers['accept-encoding'], config)
58+
, expires = new Date().getTime()
59+
, maxAge = config.maxAge;
60+
61+
fs.stat(file, (err, exists) => {
62+
if (!err && exists.isFile()) {
63+
64+
events.required([ `etag:check:${file}`, `etag:get:${file}` ], (valid) => {
65+
if (valid[0]) { // does the etag match? YES
66+
res.statusCode = unmodifiedStatus;
67+
return res.end();
68+
}
69+
// No match...
70+
res.setHeader('ETag', valid[1]); // the etag is item 2 in the array
71+
72+
if (req.method.toLowerCase() === 'head') {
73+
res.writeHead(succesStatus, {
74+
'Content-Type': mime.getType(pathname)
75+
, 'Cache-Control': `maxage=${maxAge}`
76+
, Expires: new Date(expires + maxAge).toUTCString()
77+
, 'Content-Encoding': compression
78+
});
79+
80+
res.end();
81+
} else if (not(compression === 'none')) {
82+
// we have compression!
83+
res.writeHead(succesStatus, {
84+
'Content-Type': mime.getType(pathname)
85+
, 'Cache-Control': `maxage=${maxAge}`
86+
, Expires: new Date(expires + maxAge).toUTCString()
87+
, 'Content-Encoding': compression
88+
});
89+
90+
streamFile(compression, file, connection);
91+
events.emit('static:served', pathname);
92+
} else {
93+
// no compression carry on...
94+
// return with the correct heders for the file type
95+
res.writeHead(succesStatus, {
96+
'Content-Type': mime.getType(pathname)
97+
, 'Cache-Control': `maxage=${maxAge}`
98+
, Expires: new Date(expires + maxAge).toUTCString()
99+
});
100+
fs.createReadStream(file).pipe(res);
101+
events.emit('static:served', pathname);
102+
}
103+
});
104+
105+
events.emit('etag:check', { file: file, etag: req.headers['if-none-match'] });
106+
107+
} else {
108+
events.emit('static:missing', pathname);
109+
events.emit('error:404', connection);
110+
}
111+
});
112+
};

routes/router.js

Lines changed: 17 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,40 @@
11
'use strict';
22

33
const path = require('path')
4-
, fs = require('fs')
5-
, zlib = require('zlib')
64
, events = require('harken')
75
, mime = require('mime')
8-
, brotli = require('iltorb')
96
, routeStore = require('./routeStore')
107
, matchSimpleRoute = require('./matchSimpleRoute')
118
, isWildCardRoute = require('./isWildCardRoute')
129
, parseWildCardRoute = require('./parseWildCardRoute')
1310
, setupStaticRoutes = require('./serverSetup')
1411
, setSecurityHeaders = require('../security')
12+
, handleStaticFile = require('./handleStaticFile')
1513

16-
, not = require('../utils').not
1714
, send = require('../utils').send
1815
, setStatus = require('../utils').setStatus
1916
, parsePath = require('../utils').parsePath
20-
, getCompression = require('../utils').getCompression
2117
, redirect = require('../utils').redirect
2218
, contains = require('../utils').contains
2319

2420
, statsd = require('../utils/statsd')
2521

26-
, succesStatus = 200
27-
, unmodifiedStatus = 304;
22+
, succesStatus = 200;
2823

2924
module.exports = (routesJson, config) => {
3025
const publicPath = config.publicPath
31-
, maxAge = config.maxAge
3226
, routePath = config.routePath
3327
, publicFolders = setupStaticRoutes(routePath, publicPath)
34-
, statsdClient = config.statsd === false ? false : statsd.create(config.statsd);
28+
, statsdClient = config.statsd === false ? false : statsd.create(config.statsd)
29+
30+
, setupStatsdListeners = (res, sendStatsd, cleanup) => {
31+
if (statsdClient) {
32+
// Add response listeners
33+
res.once('finish', sendStatsd);
34+
res.once('error', cleanup);
35+
res.once('close', cleanup);
36+
}
37+
};
3538

3639
routeStore.parse(routesJson);
3740

@@ -41,14 +44,13 @@ module.exports = (routesJson, config) => {
4144
, pathParsed = parsePath(req.url)
4245
, pathname = pathParsed.pathname
4346
, simpleRoute = matchSimpleRoute(pathname, method, routeStore.getStandard())
44-
, expires = new Date().getTime()
4547
, connection = {
4648
req: req
4749
, res: resIn
4850
, query: pathParsed.query
4951
, params: {}
52+
, path: pathParsed
5053
}
51-
, compression = getCompression(req.headers['accept-encoding'], config)
5254
, statsdStartTime = new Date().getTime()
5355

5456
, cleanupStatsd = () => {
@@ -86,25 +88,18 @@ module.exports = (routesJson, config) => {
8688
cleanupStatsd();
8789
};
8890

89-
let file
90-
, routeInfo
91+
let routeInfo
9192
, res = resIn;
9293

93-
// set up the statsd timing listeners
94-
if (statsdClient) {
95-
// Add response listeners
96-
res.once('finish', sendStatsd);
97-
res.once('error', cleanupStatsd);
98-
res.once('close', cleanupStatsd);
99-
}
94+
// set up the statsd timing listeners
95+
setupStatsdListeners(res, sendStatsd, cleanupStatsd);
10096

10197
// add .setStatus to response
10298
res.setStatus = setStatus;
10399

104100
// add .send to the response
105101
res.send = send(req, config);
106102
res.redirect = redirect(req);
107-
108103
res = setSecurityHeaders(config, req, res);
109104

110105
// match the first part of the url... for public stuff
@@ -115,101 +110,8 @@ module.exports = (routesJson, config) => {
115110
// the accept-encoding header. So (gzip/deflate/no compression)
116111
res.setHeader('Vary', 'Accept-Encoding');
117112

118-
file = path.join(publicPath, pathname);
119113
// read in the file and stream it to the client
120-
fs.stat(file, (err, exists) => {
121-
if (!err && exists.isFile()) {
122-
123-
events.required([ `etag:check:${file}`, `etag:get:${file}` ], (valid) => {
124-
if (valid[0]) { // does the etag match? YES
125-
res.statusCode = unmodifiedStatus;
126-
return res.end();
127-
}
128-
// No match...
129-
res.setHeader('ETag', valid[1]); // the etag is item 2 in the array
130-
131-
if (req.method.toLowerCase() === 'head') {
132-
res.writeHead(succesStatus, {
133-
'Content-Type': mime.getType(pathname)
134-
, 'Cache-Control': `maxage=${maxAge}`
135-
, Expires: new Date(expires + maxAge).toUTCString()
136-
, 'Content-Encoding': compression
137-
});
138-
139-
res.end();
140-
} else if (not(compression === 'none')) {
141-
// we have compression!
142-
res.writeHead(succesStatus, {
143-
'Content-Type': mime.getType(pathname)
144-
, 'Cache-Control': `maxage=${maxAge}`
145-
, Expires: new Date(expires + maxAge).toUTCString()
146-
, 'Content-Encoding': compression
147-
});
148-
149-
if (compression === 'deflate') {
150-
fs.stat(`${file}.def`, (errDef, existsDef) => {
151-
if (!errDef && existsDef.isFile()) {
152-
fs.createReadStream(`${file}.def`).pipe(res);
153-
} else {
154-
// no compressed file yet...
155-
fs.createReadStream(file).pipe(zlib.createDeflate())
156-
.pipe(res);
157-
158-
fs.createReadStream(file).pipe(zlib.createDeflate())
159-
.pipe(fs.createWriteStream(`${file}.def`));
160-
}
161-
});
162-
} else if (compression === 'br') {
163-
// brotli compression handling
164-
fs.stat(`${file}.brot`, (errBrotli, existsBrotli) => {
165-
if (!errBrotli && existsBrotli.isFile()) {
166-
fs.createReadStream(`${file}.brot`).pipe(res);
167-
} else {
168-
// no compressed file yet...
169-
fs.createReadStream(file).pipe(brotli.compressStream())
170-
.pipe(res);
171-
172-
fs.createReadStream(file).pipe(brotli.compressStream())
173-
.pipe(fs.createWriteStream(`${file}.brot`));
174-
}
175-
});
176-
} else {
177-
fs.stat(`${file}.tgz`, (errTgz, existsTgz) => {
178-
if (!errTgz && existsTgz.isFile()) {
179-
fs.createReadStream(`${file}.tgz`).pipe(res);
180-
} else {
181-
// no compressed file yet...
182-
fs.createReadStream(file).pipe(zlib.createGzip())
183-
.pipe(res);
184-
185-
fs.createReadStream(file).pipe(zlib.createGzip())
186-
.pipe(fs.createWriteStream(`${file}.tgz`));
187-
}
188-
});
189-
}
190-
191-
events.emit('static:served', pathname);
192-
193-
} else {
194-
// no compression carry on...
195-
// return with the correct heders for the file type
196-
res.writeHead(succesStatus, {
197-
'Content-Type': mime.getType(pathname)
198-
, 'Cache-Control': `maxage=${maxAge}`
199-
, Expires: new Date(expires + maxAge).toUTCString()
200-
});
201-
fs.createReadStream(file).pipe(res);
202-
events.emit('static:served', pathname);
203-
}
204-
});
205-
206-
events.emit('etag:check', { file: file, etag: req.headers['if-none-match'] });
207-
208-
} else {
209-
events.emit('static:missing', pathname);
210-
events.emit('error:404', connection);
211-
}
212-
});
114+
handleStaticFile(path.join(publicPath, pathname), connection, config);
213115

214116
} else if (simpleRoute !== null) {
215117
// matches a route in the routes.json
@@ -225,7 +127,6 @@ module.exports = (routesJson, config) => {
225127
} else if (isWildCardRoute(pathname, method, routeStore.getWildcard())) {
226128
// matches a route in the routes.json file that has params
227129
routeInfo = parseWildCardRoute(pathname, routeStore.getWildcard());
228-
229130
connection.params = routeInfo.values;
230131

231132
// emit the event for the url minus params and include the params
3 Bytes
Binary file not shown.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
x��1
2-
0 �=��[J�FJ��Ф�xw}����cj�T)gġ����1��D<���������cMC��R��JG.
1+
x��1
2+
�=������i�(� &�J�����\ԖT �R�����.g#��g5�&"�&`5�5��!z8r��W���*
6 Bytes
Binary file not shown.

utils/getCompression.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,37 @@ const isDefined = require('./tools').isDefined
66
return isDefined(config.compress) && !config.compress;
77
}
88

9-
, supportsBrotli = (header) => {
9+
, supportsBrotli = (header = '') => {
1010
return header.match(/\bbr\b/) || header.match(/\bbrotli\b/);
1111
}
1212

13-
, supportsGzip = (header) => {
13+
, supportsGzip = (header = '') => {
1414
return header.match(/\bgzip\b/);
1515
}
1616

17-
, supportsDeflate = (header) => {
17+
, supportsDeflate = (header = '') => {
1818
return header.match(/\bdeflate\b/);
1919
}
2020

21-
, getCompression = (header, config) => {
22-
if (isUndefined(header) || dontCompress(config)) {
23-
return 'none';
24-
} else if (supportsBrotli(header)) {
21+
, supportsCompression = (header = '') => {
22+
if (supportsBrotli(header)) {
2523
return 'br';
2624
} else if (supportsGzip(header)) {
2725
return 'gzip';
2826
} else if (supportsDeflate(header)) {
2927
return 'deflate';
30-
} else {
28+
}
29+
30+
return 'none';
31+
}
32+
33+
, getCompression = (header, config) => {
34+
const supported = header ? supportsCompression(header) : 'none';
35+
36+
if (isUndefined(header) || dontCompress(config) || supported === 'none') {
3137
return 'none';
38+
} else {
39+
return supported;
3240
}
3341
};
3442

utils/parser.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,15 @@ const getRawBody = require('raw-body')
4242
connection.req.pipe(busboy);
4343
}
4444

45+
, getCharset = (contentType) => {
46+
return typer.parse(contentType).parameters.charset || 'UTF-8';
47+
}
48+
4549
, parser = (connection, callback, scopeIn) => { // parse out the body
4650
const contentType = connection.req.headers['content-type'] ?
4751
connection.req.headers['content-type'].split(';')[0] : 'application/json'
48-
, scope = scopeIn;
49-
50-
let encoding = 'UTF-8';
51-
52-
if (isDefined(contentType)) {
53-
encoding = typer.parse(contentType).parameters.charset || 'UTF-8';
54-
}
52+
, scope = scopeIn
53+
, encoding = isDefined(contentType) ? getCharset(contentType) : 'UTF-8';
5554

5655
if (contentType === 'multipart/form-data') {
5756
try {

0 commit comments

Comments
 (0)