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

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