node-http-server – Diff between revs 11 and 14

Subversion Repositories:
Rev:
Show entire fileRegard whitespace
Rev 11 Rev 14
Line 6... Line 6...
6   6  
7 const url = require('url'); 7 const url = require('url');
8 const path = require('path'); 8 const path = require('path');
9 const fs = require('fs'); 9 const fs = require('fs');
-   10 const mime = require('mime');
Line 10... Line 11...
10 const mime = require('mime'); 11 const auth = require("http-auth");
11   12  
12 // Checks whether userPath is a child of rootPath 13 // Checks whether userPath is a child of rootPath.
13 function isRooted(userPath, rootPath, separator) { 14 function isRooted(userPath, rootPath, separator) {
14 userPath = userPath.split(separator).filter(Boolean); 15 userPath = userPath.split(separator).filter(Boolean);
15 rootPath = rootPath.split(separator).filter(Boolean); 16 rootPath = rootPath.split(separator).filter(Boolean);
16 return userPath.length >= rootPath.length && 17 return userPath.length >= rootPath.length &&
Line 17... Line -...
17 rootPath.every((e, i) => e === userPath[i]); -  
18 } -  
19   18 rootPath.every((e, i) => e === userPath[i]);
20 module.exports = { -  
21 error: { -  
22 level: { -  
23 INFO: 1, -  
24 WARN: 2, -  
25 ERROR: 3 19 }
26 } -  
27 }, -  
28 process: (config, request, response, root, callback) => { -  
29 process.nextTick(() => { -  
30 const requestAddress = request.socket.address(); -  
31 const requestedURL = url.parse(request.url, true); -  
32   -  
33 process.nextTick(() => { -  
34 callback('Client: ' + 20  
35 requestAddress.address + ':' + -  
36 requestAddress.port + -  
37 ' accessing: ' + -  
38 requestedURL.pathname, -  
39 module.exports.error.level.INFO -  
40 ); -  
41 }); -  
42   -  
43 const trimmedPath = requestedURL -  
44 .pathname -  
45 .split('/') -  
46 .filter(Boolean) -  
47 .join('/'); -  
48 const filesystemPath = trimmedPath === '/' ? -  
49 path.join(root, trimmedPath) : -  
50 path.resolve(root, trimmedPath); 21 // Serves files.
51   -  
52 if (!isRooted(filesystemPath, root, path.sep)) { 22 function files(config, request, response, resource, callback) {
53 process.nextTick(() => { -  
54 callback('Attempted path traversal: ' + -  
55 requestAddress.address + ':' + -  
56 requestAddress.port + -  
57 ' requesting: ' + 23 // Check if the file is accessible.
58 requestedURL.pathname, -  
59 module.exports.error.level.WARN 24 process.nextTick(() => {
60 ); 25 fs.access(resource, fs.constants.R_OK, (error) => {
61 }); 26 if (error) {
62 response.statusCode = 403; 27 response.statusCode = 403;
Line -... Line 28...
-   28 response.end();
-   29 return;
-   30 }
-   31  
-   32 response.setHeader(
-   33 'Content-Type',
63 response.end(); 34 mime.lookup(resource)
64 return; 35 );
-   36  
-   37 var readStream = fs.createReadStream(resource)
65 } 38 .on('open', () => {
66   39 response.statusCode = 200;
67 fs.stat(filesystemPath, (error, stats) => { 40 readStream.pipe(response);
68 // Document does not exist. 41 })
-   42 .on('error', () => {
-   43 response.statusCode = 500;
-   44 response.end();
69 if (error) { 45 });
Line 70... Line 46...
70 response.statusCode = 404; 46  
71 response.end(); 47 });
-   48 });
72 return; 49 }
73 } 50  
74   51 // Serves a directory index.
75 switch (stats.isDirectory()) { 52 function index(config, request, response, resource, callback) {
76 case true: // Directory is requested so provide directory indexes. 53 process.nextTick(() => {
77 const root = path.resolve(filesystemPath, config.site.index); 54 const root = path.resolve(resource, config.site.index);
78 fs.stat(root, (error, stats) => { 55 fs.stat(root, (error, stats) => {
79 if (error) { 56 if (error) {
80 fs.readdir(filesystemPath, (error, paths) => { 57 fs.readdir(resource, (error, paths) => {
81 if (error) { 58 if (error) {
82 process.nextTick(() => { 59 process.nextTick(() => {
83 callback('Could not list directory: ' + 60 callback('Could not list directory: ' +
84 filesystemPath, 61 resource,
85 module.exports.error.level.ERROR 62 module.exports.error.level.ERROR
86 ); 63 );
87 }); 64 });
88 response.statusCode = 500; 65 response.statusCode = 500;
89 response.end(); 66 response.end();
90 return; 67 return;
91 } 68 }
92 process.nextTick(() => { 69 process.nextTick(() => {
93 callback('Directory listing requested for: ' + 70 callback('Directory listing requested for: ' +
94 filesystemPath, 71 resource,
Line 101... Line 78...
101 }); 78 });
Line 102... Line 79...
102   79  
103 return; 80 return;
Line 104... Line 81...
104 } 81 }
105   82  
106 fs.access(filesystemPath, fs.constants.R_OK, (error) => { 83 fs.access(resource, fs.constants.R_OK, (error) => {
107 if (error) { 84 if (error) {
108 process.nextTick(() => { 85 process.nextTick(() => {
109 callback('The server was unable to access the filesystem path: ' + 86 callback('The server was unable to access the filesystem path: ' +
110 filesystemPath, 87 resource,
111 module.exports.error.level.WARN 88 module.exports.error.level.WARN
112 ); 89 );
113 }); 90 });
114 response.statusCode = 403; 91 response.statusCode = 403;
115 response.end(); 92 response.end();
Line 116... Line 93...
116 return; 93 return;
-   94 }
-   95  
117 } 96 // Set MIME content type.
-   97 response.setHeader(
Line 118... Line 98...
118   98 'Content-Type',
119 // Set MIME content type. 99 mime.lookup(root)
120 response.setHeader('Content-Type', mime.lookup(root)); 100 );
121   101  
Line 130... Line 110...
130 }); 110 });
Line 131... Line 111...
131   111  
Line 132... Line 112...
132 }); 112 });
-   113  
-   114 });
-   115 });
133   116 }
134 }); 117  
-   118 // Determines whether the requested resource is a directory or a file.
135 break; 119 function serve(config, request, response, resource, callback) {
136 default: // Browser requesting file. 120 process.nextTick(() => {
137 // Check if the file is accessible. 121 fs.stat(resource, (error, stats) => {
138 fs.access(filesystemPath, fs.constants.R_OK, (error) => { 122 // Document does not exist.
139 if (error) { 123 if (error) {
140 response.statusCode = 403; 124 response.statusCode = 404;
141 response.end(); 125 response.end();
Line -... Line 126...
-   126 return;
-   127 }
-   128  
-   129 switch (stats.isDirectory()) {
-   130 case true: // Directory is requested so provide directory indexes.
142 return; 131 index(config, request, response, resource, callback)
-   132 break;
-   133 default: // Browser requesting file.
-   134 files(config, request, response, resource, callback);
-   135 break;
-   136 }
Line -... Line 137...
-   137 });
-   138 });
-   139 }
-   140  
-   141 module.exports = {
-   142 error: {
-   143 level: {
-   144 INFO: 1,
-   145 WARN: 2,
-   146 ERROR: 3
-   147 }
-   148 },
-   149 process: (config, request, response, root, callback) => {
-   150 process.nextTick(() => {
143 } 151 // Create digest authentication.
-   152 const authentication = auth.digest({
-   153 realm: config.auth.realm,
-   154 file: path.resolve(
-   155 path.dirname(require.main.filename),
-   156 config.auth.digest
-   157 )
-   158 });
-   159  
-   160 const requestAddress = request.socket.address();
-   161 const requestedURL = url.parse(
-   162 request.url, true
-   163 );
-   164  
-   165 const trimmedPath = requestedURL
-   166 .pathname
-   167 .split('/')
-   168 .filter(Boolean)
-   169 .join('/');
-   170 const resource = trimmedPath === '/' ?
144   171 path.join(root, trimmedPath) :
145 response.setHeader('Content-Type', mime.lookup(filesystemPath)); 172 path.resolve(root, trimmedPath);
146   173  
147 var readStream = fs.createReadStream(filesystemPath) 174 if (!isRooted(resource, root, path.sep)) {
148 .on('open', () => { 175 process.nextTick(() => {
149 response.statusCode = 200; 176 callback('Attempted path traversal: ' +
150 readStream.pipe(response); 177 requestAddress.address + ':' +
-   178 requestAddress.port +
151 }) 179 ' requesting: ' +
-   180 requestedURL.pathname,
-   181 module.exports.error.level.WARN
-   182 );
-   183 });
Line -... Line 184...
-   184 response.statusCode = 404;
-   185 response.end();
-   186 return;
-   187 }
-   188  
-   189 switch (config.auth.locations.some(
-   190 (authPath) => authPath.toUpperCase() === requestedURL.pathname.toUpperCase())) {
-   191 case true:
-   192 // Requested location requires authentication.
-   193 authentication.check(request, response, (request, response) => {
-   194 process.nextTick(() => {
-   195 callback('Authenticated client: ' +
-   196 requestAddress.address + ':' +
-   197 requestAddress.port +
-   198 ' accessing: ' +
152 .on('error', () => { 199 requestedURL.pathname,
153 response.statusCode = 500; 200 module.exports.error.level.INFO
-   201 );
-   202 });
-   203 serve(config, request, response, resource, callback);
-   204 });
-   205 break;
-   206 default:
-   207 process.nextTick(() => {
-   208 callback('Client: ' +
-   209 requestAddress.address + ':' +
-   210 requestAddress.port +
-   211 ' accessing: ' +
-   212 requestedURL.pathname,
154 response.end(); 213 module.exports.error.level.INFO
155 }); -  
156   214 );
157 }); 215 });
158 break; 216 serve(config, request, response, resource, callback);