node-http-server – Diff between revs 29 and 30

Subversion Repositories:
Rev:
Show entire fileRegard whitespace
Rev 29 Rev 30
Line 7... Line 7...
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'); 10 const mime = require('mime');
11 const auth = require("http-auth"); 11 const auth = require("http-auth");
12 const JSONStream = require('JSONStream'); 12 const stream = require('stream');
Line 13... Line 13...
13   13  
14 // Checks whether userPath is a child of rootPath. 14 // Checks whether userPath is a child of rootPath.
15 function isRooted(userPath, rootPath, separator, callback) { 15 function isRooted(userPath, rootPath, separator, callback) {
16 userPath = userPath.split(separator).filter(Boolean); 16 userPath = userPath.split(separator).filter(Boolean);
Line 22... Line 22...
22 // Serves files. 22 // Serves files.
23 function files(config, file, client, callback) { 23 function files(config, file, client, callback) {
24 // Check if the file is accessible. 24 // Check if the file is accessible.
25 fs.access(file, fs.constants.R_OK, (error) => { 25 fs.access(file, fs.constants.R_OK, (error) => {
26 if (error) { 26 if (error) {
27 process.nextTick(() => { -  
28 callback({ 27 callback({
29 message: 'Client: ' + 28 message: 'Client: ' +
30 client.address + ':' + 29 client.address + ':' +
31 client.port + 30 client.port +
32 ' requesting inaccessible path: ' + 31 ' requesting inaccessible path: ' +
33 file, 32 file,
34 severity: 'warning', 33 severity: 'warning'
-   34 }, {
35 status: 403 35 status: 403,
-   36 data: new stream.Readable({
-   37 read(size) {
-   38 this.push(null);
36 39 }
37 }); 40 }),
-   41 type: 'text/plain'
38 }); 42 });
39 return; 43 return;
40 } 44 }
41 process.nextTick(() => { -  
42 callback({ 45 callback({
43 message: 'Client: ' + 46 message: 'Client: ' +
44 client.address + ':' + 47 client.address + ':' +
45 client.port + 48 client.port +
46 ' sent file: ' + 49 ' sent file: ' +
47 file, 50 file,
48 severity: 'info', 51 severity: 'info'
-   52 }, {
49 status: 200, 53 status: 200,
50 data: fs 54 data: fs
51 .createReadStream(file), 55 .createReadStream(file),
52 type: mime 56 type: mime
53 .lookup(file) 57 .lookup(file)
54 }); 58 });
55 }); 59 });
56   -  
57 }); -  
58 } 60 }
Line 59... Line 61...
59   61  
60 // Serves a directory listing or the document index in case it exists. 62 // Serves a directory listing or the document index in case it exists.
61 function index(config, directory, href, client, callback) { 63 function index(config, directory, href, client, callback) {
Line 65... Line 67...
65 if (config.site.indexing 67 if (config.site.indexing
66 .some((directory) => 68 .some((directory) =>
67 directory.toUpperCase() === href.toUpperCase())) { 69 directory.toUpperCase() === href.toUpperCase())) {
68 fs.readdir(directory, (error, paths) => { 70 fs.readdir(directory, (error, paths) => {
69 if (error) { 71 if (error) {
70 process.nextTick(() => { 72 console.log("listing forbidden...");
71 callback({ 73 callback({
72 message: 'Client: ' + 74 message: 'Client: ' +
73 client.address + ':' + 75 client.address + ':' +
74 client.port + 76 client.port +
75 ' could not access directory: ' + 77 ' could not access directory: ' +
76 directory, 78 directory,
77 severity: 'warning', 79 severity: 'warning'
-   80 }, {
78 status: 500 81 status: 500,
-   82 data: new stream.Readable({
-   83 read(size) {
-   84 this.push(null);
-   85 }
79 }); 86 }),
-   87 type: 'text/plain'
80 }); 88 });
81 return; 89 return;
82 } 90 }
83 process.nextTick(() => { 91 console.log("sending listing...");
84 callback({ 92 callback({
85 message: 'Client: ' + 93 message: 'Client: ' +
86 client.address + ':' + 94 client.address + ':' +
87 client.port + 95 client.port +
88 ' accessed directory listing: ' + 96 ' accessed directory listing: ' +
89 directory, 97 directory,
90 severity: 'warning', 98 severity: 'info'
-   99 }, {
91 status: 200, 100 status: 200,
92 data: JSONStream.parse(paths) 101 data: new stream.Readable({
-   102 read(size) {
-   103 this.push(JSON.stringify(paths));
-   104 this.push(null);
-   105 }
93 }); 106 }),
-   107 type: 'application/json'
94 }); 108 });
95 }); 109 });
96 return; 110 return;
97 } 111 }
98 // Could not access directory index file and directory listing not allowed. 112 // Could not access directory index file and directory listing not allowed.
99 process.nextTick(() => { 113 console.log("no dirindex...");
100 callback({ 114 callback({
101 message: 'Client: ' + 115 message: 'Client: ' +
102 client.address + ':' + 116 client.address + ':' +
103 client.port + 117 client.port +
104 ' no index file found and accessing forbiden index: ' + 118 ' no index file found and accessing forbiden index: ' +
105 href, 119 href,
106 severity: 'warning', 120 severity: 'warning'
-   121 }, {
107 status: 400 122 status: 403,
-   123 data: new stream.Readable({
-   124 read(size) {
-   125 this.push(null);
-   126 }
108 }); 127 }),
-   128 type: 'text/plain'
109 }); 129 });
110 return; 130 return;
111   -  
112 } 131 }
Line 113... Line 132...
113   132  
114 // Serve the document index. 133 // Serve the document index.
115 fs.access(root, fs.constants.R_OK, (error) => { 134 fs.access(root, fs.constants.R_OK, (error) => {
116 if (error) { -  
117 process.nextTick(() => { 135 if (error) {
118 callback({ 136 callback({
119 message: 'Client: ' + 137 message: 'Client: ' +
120 client.address + ':' + 138 client.address + ':' +
121 client.port + 139 client.port +
122 ' unable to access path: ' + 140 ' unable to access path: ' +
123 directory, 141 directory,
-   142 severity: 'warning'
124 severity: 'warning', 143 }, {
-   144 status: 403,
-   145 data: new stream.Readable({
-   146 read(size) {
-   147 this.push(null);
125 status: 403 148 }
-   149 }),
126 }); 150 type: 'text/plain'
127 }); 151 });
128 return; 152 return;
129 } -  
130 process.nextTick(() => { 153 }
131 callback({ 154 callback({
132 message: 'Client: ' + 155 message: 'Client: ' +
133 client.address + ':' + 156 client.address + ':' +
134 client.port + 157 client.port +
135 ' sent file: ' + 158 ' sent file: ' +
136 root, 159 root,
-   160 severity: 'info'
137 severity: 'info', 161 }, {
138 status: 200, 162 status: 200,
139 data: fs.createReadStream(root), 163 data: fs.createReadStream(root),
140 type: mime.lookup(root) 164 type: mime.lookup(root)
141 }); 165 });
142 }); 166 });
143 }); -  
144 }); 167 });
Line 145... Line 168...
145 } 168 }
146   169  
147 // Determines whether the requested filesystem request path is a directory or a file. 170 // Determines whether the requested filesystem request path is a directory or a file.
Line 153... Line 176...
153 message: 'Client: ' + 176 message: 'Client: ' +
154 address.address + ':' + 177 address.address + ':' +
155 address.port + 178 address.port +
156 ' accessing non-existent document: ' + 179 ' accessing non-existent document: ' +
157 local, 180 local,
158 severity: 'warning', 181 severity: 'warning'
-   182 }, {
159 status: 404 183 status: 404,
-   184 data: new stream.Readable({
-   185 read(size) {
-   186 this.push(null);
-   187 }
-   188 }),
-   189 type: 'text/plain'
160 }); 190 });
161 return; 191 return;
162 } 192 }
Line 163... Line 193...
163   193  
Line 171... Line 201...
171   201  
172 // If the file matches the reject list or is not in the accept list, 202 // If the file matches the reject list or is not in the accept list,
173 // then there is no file to serve. 203 // then there is no file to serve.
174 if (config.site.reject.some((expression) => expression.test(file)) || 204 if (config.site.reject.some((expression) => expression.test(file)) ||
175 !config.site.accept.some((expression) => expression.test(file))) { -  
176 process.nextTick(() => { 205 !config.site.accept.some((expression) => expression.test(file))) {
177 callback({ 206 callback({
178 message: 'Client: ' + 207 message: 'Client: ' +
179 address.address + ':' + 208 address.address + ':' +
180 address.port + 209 address.port +
181 ' requested disallowed file: ' + 210 ' requested disallowed file: ' +
182 file, 211 file,
-   212 severity: 'warning'
183 severity: 'warning', 213 }, {
-   214 status: 404,
-   215 data: new stream.Readable({
-   216 read(size) {
-   217 this.push(null);
184 status: 404 218 }
-   219 }),
185 }); 220 type: 'text/plain'
186 }); 221 });
187 return; 222 return;
Line 188... Line 223...
188 } 223 }
Line 193... Line 228...
193 }); 228 });
194 } 229 }
Line 195... Line 230...
195   230  
196 module.exports = { 231 module.exports = {
197 process: (config, request, response, root, callback) => { -  
198 process.nextTick(() => { 232 process: (config, request, response, root, callback) => {
199 const requestAddress = request.socket.address(); 233 const requestAddress = request.socket.address();
200 const requestURL = url.parse( 234 const requestURL = url.parse(
201 request.url, true 235 request.url, true
Line 239... Line 273...
239 path.resolve(root, trimmedPath); 273 path.resolve(root, trimmedPath);
Line 240... Line 274...
240   274  
241 fs.realpath(requestPath, (error, resolvedPath) => { 275 fs.realpath(requestPath, (error, resolvedPath) => {
242 // If the path does not exist, then return early. 276 // If the path does not exist, then return early.
243 if (error) { -  
244 process.nextTick(() => { 277 if (error) {
245 callback({ 278 callback({
246 message: 'Unknown path requested: ' + 279 message: 'Unknown path requested: ' +
247 requestAddress.address + ':' + 280 requestAddress.address + ':' +
248 requestAddress.port + 281 requestAddress.port +
249 ' requesting: ' + 282 ' requesting: ' +
250 requestURL.pathname, 283 requestURL.pathname,
-   284 severity: 'warning'
251 severity: 'warning', 285 }, {
-   286 status: 404,
-   287 data: new stream.Readable({
-   288 read(size) {
-   289 this.push(null);
252 status: 404 290 }
-   291 }),
253 }); 292 type: 'text/plain'
254 }); 293 });
255 return; 294 return;
256 } 295 }
257 // Check for path traversals early on and bail if the requested path does not 296 // Check for path traversals early on and bail if the requested path does not
258 // lie within the specified document root. 297 // lie within the specified document root.
259 isRooted(resolvedPath, root, path.sep, (rooted) => { 298 isRooted(resolvedPath, root, path.sep, (rooted) => {
260 if (!rooted) { -  
261 process.nextTick(() => { 299 if (!rooted) {
262 callback({ 300 callback({
263 message: 'Attempted path traversal: ' + 301 message: 'Attempted path traversal: ' +
264 requestAddress.address + ':' + 302 requestAddress.address + ':' +
265 requestAddress.port + 303 requestAddress.port +
266 ' requesting: ' + 304 ' requesting: ' +
267 requestURL.pathname, 305 requestURL.pathname,
-   306 severity: 'warning'
268 severity: 'warning', 307 }, {
-   308 status: 404,
-   309 data: new stream.Readable({
-   310 read(size) {
-   311 this.push(null);
269 status: 404 312 }
-   313 }),
270 }); 314 type: 'text/plain'
271 }); 315 });
272 return; 316 return;
Line 273... Line 317...
273 } 317 }
Line 283... Line 327...
283 config.auth.digest 327 config.auth.digest
284 ) 328 )
285 }); 329 });
286 // Requested location requires authentication. 330 // Requested location requires authentication.
287 authentication.check(request, response, (request, response) => { 331 authentication.check(request, response, (request, response) => {
288 process.nextTick(() => { -  
289 callback({ 332 callback({
290 message: 'Authenticated client: ' + 333 message: 'Authenticated client: ' +
291 requestAddress.address + ':' + 334 requestAddress.address + ':' +
292 requestAddress.port + 335 requestAddress.port +
293 ' accessing: ' + 336 ' accessing: ' +
294 requestURL.pathname, 337 requestURL.pathname,
295 severity: 'info' 338 severity: 'info'
296 }); 339 });
297 }); -  
298 serve(config, 340 serve(config,
299 requestPath, 341 requestPath,
300 requestURL.pathname, 342 requestURL.pathname,
301 requestAddress, 343 requestAddress,
302 callback 344 callback
Line 304... Line 346...
304 }); 346 });
305 return; 347 return;
306 } 348 }
Line 307... Line 349...
307   349  
308 // If no authentication is required then serve the request. -  
309 process.nextTick(() => { 350 // If no authentication is required then serve the request.
310 callback({ 351 callback({
311 message: 'Client: ' + 352 message: 'Client: ' +
312 requestAddress.address + ':' + 353 requestAddress.address + ':' +
313 requestAddress.port + 354 requestAddress.port +
314 ' accessing: ' + 355 ' accessing: ' +
315 requestURL.pathname, 356 requestURL.pathname,
316 severity: 'info' 357 severity: 'info'
317 }); -  
318 }); 358 });
319 serve(config, 359 serve(config,
320 requestPath, 360 requestPath,
321 requestURL.pathname, 361 requestURL.pathname,
322 requestAddress, 362 requestAddress,
323 callback 363 callback
324 ); 364 );
325 }); 365 });
326 }); -  
327 }); 366 });
328 } 367 }