node-http-server – Diff between revs 7 and 8

Subversion Repositories:
Rev:
Show entire fileIgnore whitespace
Rev 7 Rev 8
Line 1... Line 1...
1 #!/usr/bin/env node 1 #!/usr/bin/env node
2   -  
3 /////////////////////////////////////////////////////////////////////////// 2 ///////////////////////////////////////////////////////////////////////////
4 // Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 // 3 // Copyright (C) 2017 Wizardry and Steamworks - License: GNU GPLv3 //
5 /////////////////////////////////////////////////////////////////////////// 4 ///////////////////////////////////////////////////////////////////////////
Line 6... Line 5...
6   5  
7 // Import packages. 6 // Import packages.
8 const auth = require("http-auth"); 7 const auth = require("http-auth");
9 const https = require('https'); 8 const https = require('https');
10 const http = require('http'); 9 const http = require('http');
11 const path = require('path'); 10 const path = require('path');
12 const fs = require('fs'); -  
13 const mime = require('mime'); 11 const fs = require('fs');
14 const url = require('url'); 12 const url = require('url');
15 const moment = require('moment'); 13 const moment = require('moment');
16 const winston = require('winston'); 14 const winston = require('winston');
17 const yargs = require('yargs'); -  
18 const forge = require('node-forge'); 15 const yargs = require('yargs');
Line -... Line 16...
-   16 const dns = require('dns');
-   17  
-   18 // Local imports.
-   19 const handler = require(
-   20 path
-   21 .resolve(__dirname, 'src', 'handler')
-   22 );
-   23 const certs = require(
-   24 path
-   25 .resolve(__dirname, 'src', 'certs')
-   26 );
-   27  
-   28 // Configuration file.
-   29 const config = require(
-   30 path
-   31 .resolve(__dirname, 'config')
19 const dns = require('dns'); 32 );
20   33  
21 // Get command-line arguments. 34 // Get command-line arguments.
22 const argv = yargs 35 const argv = yargs
23 .version() 36 .version()
Line 27... Line 40...
27 demandOption: true 40 demandOption: true
28 }) 41 })
29 .help() 42 .help()
30 .argv 43 .argv
Line 31... Line -...
31   -  
32 // Configuration file. -  
33 const config = require( -  
34 path -  
35 .resolve(__dirname, 'config') -  
36 ); -  
37   -  
38 // Check for path traversal. -  
39 function isRooted(userPath, rootPath, separator) { -  
40 userPath = userPath.split(separator).filter(Boolean); -  
41 rootPath = rootPath.split(separator).filter(Boolean); -  
42 return userPath.length >= rootPath.length && -  
43 rootPath.every((e, i) => e === userPath[i]); -  
44 } -  
45   -  
46 // Generate certificates on the fly using incremental serials. -  
47 function generateCertificates(name, domain, size) { -  
48 // Generate 1024-bit key-pair. -  
49 const keys = forge -  
50 .pki -  
51 .rsa -  
52 .generateKeyPair(size); -  
53 // Create self-signed certificate. -  
54 const cert = forge -  
55 .pki -  
56 .createCertificate(); -  
57 cert.serialNumber = moment().format('x'); -  
58 cert.publicKey = keys.publicKey; -  
59 cert -  
60 .validity -  
61 .notBefore = moment().toDate(); -  
62 cert -  
63 .validity -  
64 .notAfter -  
65 .setFullYear( -  
66 cert -  
67 .validity -  
68 .notBefore -  
69 .getFullYear() + 1 -  
70 ); -  
71 cert.setSubject([{ -  
72 name: 'commonName', -  
73 value: domain -  
74 }, { -  
75 name: 'organizationName', -  
76 value: name -  
77 }]); -  
78 cert.setIssuer([{ -  
79 name: 'commonName', -  
80 value: domain -  
81 }, { -  
82 name: 'organizationName', -  
83 value: name -  
84 }]); -  
85   -  
86 // Self-sign certificate. -  
87 cert.sign( -  
88 keys.privateKey, -  
89 forge -  
90 .md -  
91 .sha256 -  
92 .create() -  
93 ); -  
94   -  
95 // Return PEM-format keys and certificates. -  
96 return { -  
97 privateKey: forge -  
98 .pki -  
99 .privateKeyToPem( -  
100 keys -  
101 .privateKey -  
102 ), -  
103 publicKey: forge -  
104 .pki -  
105 .publicKeyToPem( -  
106 keys -  
107 .publicKey -  
108 ), -  
109 certificate: forge -  
110 .pki -  
111 .certificateToPem(cert) -  
112 }; -  
113 } -  
114   44  
115 // Create various logging mechanisms. 45 // Create various logging mechanisms.
116 const log = new winston.Logger({ 46 const log = new winston.Logger({
117 transports: [ 47 transports: [
118 new winston.transports.File({ 48 new winston.transports.File({
Line 134... Line 64...
134 }) 64 })
135 ], 65 ],
136 exitOnError: false 66 exitOnError: false
137 }); 67 });
Line 138... Line -...
138   -  
139 function handleClient(request, response, documentRoot) { -  
140 const requestAddress = request.socket.address(); -  
141 const requestedURL = url.parse(request.url, true); -  
142   -  
143 log.info('Client: ' + -  
144 requestAddress.address + ':' + -  
145 requestAddress.port + -  
146 ' accessing: ' + -  
147 requestedURL.pathname -  
148 ); -  
149   -  
150 const trimmedPath = requestedURL -  
151 .pathname -  
152 .split('/') -  
153 .filter(Boolean) -  
154 .join('/'); -  
155 const filesystemPath = trimmedPath === '/' ? -  
156 path.join(documentRoot, trimmedPath) : -  
157 path.resolve(documentRoot, trimmedPath); -  
158   -  
159 if (!isRooted(filesystemPath, documentRoot, path.sep)) { -  
160 log.warn('Attempted path traversal: ' + -  
161 requestAddress.address + ':' + -  
162 requestAddress.port + -  
163 ' requesting: ' + -  
164 requestedURL.pathname -  
165 ); -  
166 response.statusCode = 403; -  
167 response.end(); -  
168 return; -  
169 } -  
170   -  
171 fs.stat(filesystemPath, (error, stats) => { -  
172 // Document does not exist. -  
173 if (error) { -  
174 response.statusCode = 404; -  
175 response.end(); -  
176 return; -  
177 } -  
178   -  
179 switch (stats.isDirectory()) { -  
180 case true: // Directory is requested so provide directory indexes. -  
181 const documentRoot = path.resolve(filesystemPath, config.site.index); -  
182 fs.stat(documentRoot, (error, stats) => { -  
183 if (error) { -  
184 fs.readdir(filesystemPath, (error, paths) => { -  
185 if (error) { -  
186 log.warn('Could not list directory: ' + filesystemPath); -  
187 response.statusCode = 500; -  
188 response.end(); -  
189 return; -  
190 } -  
191 log.info('Directory listing requested for: ' + filesystemPath); -  
192 response.statusCode = 200; -  
193 response.write(JSON.stringify(paths)); -  
194 response.end(); -  
195 }); -  
196   -  
197 return; -  
198 } -  
199   -  
200 fs.access(filesystemPath, fs.constants.R_OK, (error) => { -  
201 if (error) { -  
202 log.warn('The server was unable to access the filesystem path: ' + filesystemPath); -  
203 response.statusCode = 403; -  
204 response.end(); -  
205 return; -  
206 } -  
207   -  
208 // Set MIME content type. -  
209 response.setHeader('Content-Type', mime.lookup(documentRoot)); -  
210   -  
211 var readStream = fs.createReadStream(documentRoot) -  
212 .on('open', () => { -  
213 response.statusCode = 200; -  
214 readStream.pipe(response); -  
215 }) -  
216 .on('error', () => { -  
217 response.statusCode = 500; -  
218 response.end(); -  
219 }); -  
220   -  
221 }); -  
222   -  
223 }); -  
224 break; -  
225 default: // Browser requesting file. -  
226 // Check if the file is accessible. -  
227 fs.access(filesystemPath, fs.constants.R_OK, (error) => { -  
228 if (error) { -  
229 response.statusCode = 403; -  
230 response.end(); -  
231 return; -  
232 } -  
233   -  
234 response.setHeader('Content-Type', mime.lookup(filesystemPath)); -  
235   -  
236 var readStream = fs.createReadStream(filesystemPath) -  
237 .on('open', () => { -  
238 response.statusCode = 200; -  
239 readStream.pipe(response); -  
240 }) -  
241 .on('error', () => { -  
242 response.statusCode = 500; -  
243 response.end(); -  
244 }); -  
245   -  
246 }); -  
247 break; -  
248 } -  
249 }); -  
250 } -  
251   68  
252 fs.realpath(argv.root, (error, documentRoot) => { 69 fs.realpath(argv.root, (error, root) => {
253 if (error) { 70 if (error) {
254 log.error('Could not find document root: ' + argv.root); 71 log.error('Could not find document root: ' + argv.root);
255 process.exit(1); 72 process.exit(1);
Line 263... Line 80...
263   80  
264 // Start HTTP server. 81 // Start HTTP server.
265 http.createServer( 82 http.createServer(
266 // authentication, 83 // authentication,
267 (request, response) => 84 (request, response) =>
-   85 handler.handleClient(config, request, response, root, (error, level) => {
-   86 switch (level) {
-   87 case handler.error.level.INFO:
-   88 log.info(error);
-   89 break;
-   90 case handler.error.level.WARN:
-   91 log.warn(error);
-   92 break;
-   93 case handler.error.level.ERROR:
-   94 log.error(error);
-   95 break;
-   96 }
268 handleClient(request, response, documentRoot) 97 })
269 ).listen(config.net.port, config.net.address, () => { 98 ).listen(config.net.port, config.net.address, () => {
270 log.info('HTTP Server is listening on: ' + 99 log.info('HTTP Server is listening on: ' +
271 config.net.address + 100 config.net.address +
272 ' and port: ' + 101 ' and port: ' +
273 config.net.port + 102 config.net.port +
274 ' whilst serving files from: ' + 103 ' whilst serving files from: ' +
275 documentRoot 104 root
276 ); 105 );
Line 277... Line 106...
277 }); 106 });
278   107  
279 // Start HTTPs server if enabled. 108 // Start HTTPs server if enabled.
280 config.ssl.enable && (() => { 109 if (config.ssl.enable) {
281 // Generate certificates for HTTPs. 110 // Generate certificates for HTTPs.
282 const certs = generateCertificates( 111 certs.generateCertificates(
283 config.site.name, 112 config.site.name,
-   113 config.net.address,
-   114 config.ssl.privateKeySize,
-   115 (certificates) => {
-   116 https.createServer(
-   117 // authentication,
-   118 {
-   119 key: certificates.privateKey,
-   120 cert: certificates.certificate,
-   121 },
-   122 (request, response) =>
-   123 handler.handleClient(config, request, response, root, (error, level) => {
-   124 switch (level) {
-   125 case handler.error.level.INFO:
-   126 log.info(error);
-   127 break;
-   128 case handler.error.level.WARN:
-   129 log.warn(error);
-   130 break;
-   131 case handler.error.level.ERROR:
-   132 log.error(error);
-   133 break;
-   134 }
-   135 })
-   136 ).listen(config.ssl.port, config.ssl.address, () => {
-   137 // Print information on server startup.
-   138 log.info('HTTPs Server is listening on: ' +
-   139 config.net.address +
-   140 ' and port: ' +
-   141 config.net.port +
-   142 ' whilst serving files from: ' +
-   143 root
-   144 );
284 config.net.address, 145 })
285 config.ssl.privateKeySize -  
286 ); -  
287   -  
288 https.createServer( -  
289 // authentication, -  
290 { -  
291 key: certs.privateKey, -  
292 cert: certs.certificate, -  
293 }, -  
294 (request, response) => -  
295 handleClient(request, response, documentRoot) -  
296 ).listen(config.ssl.port, config.ssl.address, () => { -  
297 // Print information on server startup. -  
298 log.info('HTTPs Server is listening on: ' + -  
299 config.net.address + -  
300 ' and port: ' + -  
301 config.net.port + -  
302 ' whilst serving files from: ' + -  
303 documentRoot -  
304 ); 146 }
305 }); -  
306 })(); 147 );