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

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