@@ -4,13 +4,15 @@ var sys = require('sys'),
44 url = require ( 'url' ) ,
55 events = require ( 'events' ) ;
66
7- function main ( ) {
7+ var DEFAULT_PORT = 8000 ;
8+
9+ function main ( argv ) {
810 new HttpServer ( {
911 'GET' : ( function ( ) {
1012 var servlet = new StaticServlet ( ) ;
1113 return servlet . handleRequest . bind ( servlet )
1214 } ) ( )
13- } ) . start ( 8000 ) ;
15+ } ) . start ( Number ( argv [ 2 ] ) || DEFAULT_PORT ) ;
1416}
1517
1618function escapeHtml ( value ) {
@@ -44,11 +46,15 @@ HttpServer.prototype.parseUrl_ = function(urlString) {
4446} ;
4547
4648HttpServer . prototype . handleRequest_ = function ( req , res ) {
47- sys . puts ( req . method + ' ' + req . url ) ;
49+ var logEntry = req . method + ' ' + req . url ;
50+ if ( req . headers [ 'user-agent' ] ) {
51+ logEntry += ' ' + req . headers [ 'user-agent' ] ;
52+ }
53+ sys . puts ( logEntry ) ;
4854 req . url = this . parseUrl_ ( req . url ) ;
4955 var handler = this . handlers [ req . method ] ;
5056 if ( ! handler ) {
51- res . writeHead ( 501 , 'Not Implemented' ) ;
57+ res . writeHead ( 501 ) ;
5258 res . end ( ) ;
5359 } else {
5460 handler . call ( this , req , res ) ;
@@ -79,51 +85,79 @@ StaticServlet.prototype.handleRequest = function(req, res) {
7985 var path = ( './' + req . url . pathname ) . replace ( '//' , '/' ) ;
8086 var parts = path . split ( '/' ) ;
8187 if ( parts [ parts . length - 1 ] . charAt ( 0 ) === '.' )
82- return self . sendForbidden_ ( res , path ) ;
88+ return self . sendForbidden_ ( req , res , path ) ;
8389 fs . stat ( path , function ( err , stat ) {
8490 if ( err )
85- return self . sendMissing_ ( res , path ) ;
91+ return self . sendMissing_ ( req , res , path ) ;
8692 if ( stat . isDirectory ( ) )
87- return self . sendDirectory_ ( res , path ) ;
88- return self . sendFile_ ( res , path ) ;
93+ return self . sendDirectory_ ( req , res , path ) ;
94+ return self . sendFile_ ( req , res , path ) ;
8995 } ) ;
9096}
9197
92- StaticServlet . prototype . sendError_ = function ( res , error ) {
93- res . writeHead ( 500 , 'Internal Server Error' , {
98+ StaticServlet . prototype . sendError_ = function ( req , res , error ) {
99+ res . writeHead ( 500 , {
94100 'Content-Type' : 'text/html'
95101 } ) ;
96102 res . write ( '<!doctype html>\n' ) ;
97103 res . write ( '<title>Internal Server Error</title>\n' ) ;
98- res . write ( '<h1>500 Internal Server Error</h1>' ) ;
104+ res . write ( '<h1>Internal Server Error</h1>' ) ;
99105 res . write ( '<pre>' + escapeHtml ( sys . inspect ( error ) ) + '</pre>' ) ;
100106 sys . puts ( '500 Internal Server Error' ) ;
101107 sys . puts ( sys . inspect ( error ) ) ;
102108} ;
103109
104- StaticServlet . prototype . sendMissing_ = function ( res , path ) {
105- res . writeHead ( 404 , 'Not Found' , {
110+ StaticServlet . prototype . sendMissing_ = function ( req , res , path ) {
111+ path = path . substring ( 1 ) ;
112+ res . writeHead ( 404 , {
106113 'Content-Type' : 'text/html'
107114 } ) ;
108115 res . write ( '<!doctype html>\n' ) ;
109116 res . write ( '<title>404 Not Found</title>\n' ) ;
110- res . write ( '<h1>404 Not Found</h1>' ) ;
117+ res . write ( '<h1>Not Found</h1>' ) ;
118+ res . write (
119+ '<p>The requested URL ' +
120+ escapeHtml ( path ) +
121+ ' was not found on this server.</p>'
122+ ) ;
111123 res . end ( ) ;
112124 sys . puts ( '404 Not Found: ' + path ) ;
113125} ;
114126
115- StaticServlet . prototype . sendForbidden_ = function ( res , path ) {
116- res . writeHead ( 403 , 'Forbidden' , {
127+ StaticServlet . prototype . sendForbidden_ = function ( req , res , path ) {
128+ path = path . substring ( 1 ) ;
129+ res . writeHead ( 403 , {
117130 'Content-Type' : 'text/html'
118131 } ) ;
119132 res . write ( '<!doctype html>\n' ) ;
120133 res . write ( '<title>403 Forbidden</title>\n' ) ;
121- res . write ( '<h1>403 Forbidden</h1>' ) ;
134+ res . write ( '<h1>Forbidden</h1>' ) ;
135+ res . write (
136+ '<p>You do not have permission to access ' +
137+ escapeHtml ( path ) + ' on this server.</p>'
138+ ) ;
122139 res . end ( ) ;
123140 sys . puts ( '403 Forbidden: ' + path ) ;
124141} ;
125142
126- StaticServlet . prototype . sendFile_ = function ( res , path ) {
143+ StaticServlet . prototype . sendRedirect_ = function ( req , res , redirectUrl ) {
144+ res . writeHead ( 301 , {
145+ 'Content-Type' : 'text/html' ,
146+ 'Location' : redirectUrl
147+ } ) ;
148+ res . write ( '<!doctype html>\n' ) ;
149+ res . write ( '<title>301 Moved Permanently</title>\n' ) ;
150+ res . write ( '<h1>Moved Permanently</h1>' ) ;
151+ res . write (
152+ '<p>The document has moved <a href="' +
153+ redirectUrl +
154+ '">here</a>.</p>'
155+ ) ;
156+ res . end ( ) ;
157+ sys . puts ( '401 Moved Permanently: ' + redirectUrl ) ;
158+ } ;
159+
160+ StaticServlet . prototype . sendFile_ = function ( req , res , path ) {
127161 var self = this ;
128162 var file = fs . createReadStream ( path ) ;
129163 res . writeHead ( 200 , {
@@ -135,38 +169,61 @@ StaticServlet.prototype.sendFile_ = function(res, path) {
135169 res . end ( ) ;
136170 } ) ;
137171 file . on ( 'error' , function ( error ) {
138- self . sendError_ ( res , error ) ;
172+ self . sendError_ ( req , res , error ) ;
139173 } ) ;
140174} ;
141175
142- StaticServlet . prototype . sendDirectory_ = function ( res , path ) {
176+ StaticServlet . prototype . sendDirectory_ = function ( req , res , path ) {
143177 var self = this ;
178+ if ( path . match ( / [ ^ \/ ] $ / ) ) {
179+ req . url . pathname += '/' ;
180+ var redirectUrl = url . format ( url . parse ( url . format ( req . url ) ) ) ;
181+ return self . sendRedirect_ ( req , res , redirectUrl ) ;
182+ }
144183 fs . readdir ( path , function ( err , files ) {
145184 if ( err )
146- return self . sendError_ ( res , error ) ;
147- res . writeHead ( 200 , {
148- 'Content-Type' : 'text/html'
185+ return self . sendError_ ( req , res , error ) ;
186+
187+ if ( ! files . length )
188+ return self . writeDirectoryIndex_ ( req , res , path , [ ] ) ;
189+
190+ var remaining = files . length ;
191+ files . forEach ( function ( fileName , index ) {
192+ fs . stat ( path + '/' + fileName , function ( err , stat ) {
193+ if ( err )
194+ return self . sendError_ ( req , res , err ) ;
195+ if ( stat . isDirectory ( ) ) {
196+ files [ index ] = fileName + '/' ;
197+ }
198+ if ( ! ( -- remaining ) )
199+ return self . writeDirectoryIndex_ ( req , res , path , files ) ;
200+ } ) ;
149201 } ) ;
150- path = path . substring ( 2 ) ;
151- res . write ( '<!doctype html>\n' ) ;
152- res . write ( '<title>' + escapeHtml ( path ) + '</title>\n' ) ;
153- res . write ( '<style>\n' ) ;
154- res . write ( ' ol { list-style-type: none; font-size: 1.2em; }\n' ) ;
155- res . write ( '</style>\n' ) ;
156- res . write ( '<h1>Directory: ' + escapeHtml ( path ) + '</h1>' ) ;
157- res . write ( '<ol>' ) ;
158- files . sort ( ) ;
159- for ( var i = 0 ; i < files . length ; ++ i ) {
160- if ( files [ i ] . charAt ( 0 ) !== '.' ) {
161- res . write ( '<li><a href="' +
162- escapeHtml ( files [ i ] ) + '">' +
163- escapeHtml ( files [ i ] ) + '</a></li>' ) ;
164- }
202+ } ) ;
203+ } ;
204+
205+ StaticServlet . prototype . writeDirectoryIndex_ = function ( req , res , path , files ) {
206+ path = path . substring ( 1 ) ;
207+ res . writeHead ( 200 , {
208+ 'Content-Type' : 'text/html'
209+ } ) ;
210+ res . write ( '<!doctype html>\n' ) ;
211+ res . write ( '<title>' + escapeHtml ( path ) + '</title>\n' ) ;
212+ res . write ( '<style>\n' ) ;
213+ res . write ( ' ol { list-style-type: none; font-size: 1.2em; }\n' ) ;
214+ res . write ( '</style>\n' ) ;
215+ res . write ( '<h1>Directory: ' + escapeHtml ( path ) + '</h1>' ) ;
216+ res . write ( '<ol>' ) ;
217+ files . forEach ( function ( fileName ) {
218+ if ( fileName . charAt ( 0 ) !== '.' ) {
219+ res . write ( '<li><a href="' +
220+ escapeHtml ( fileName ) + '">' +
221+ escapeHtml ( fileName ) + '</a></li>' ) ;
165222 }
166- res . write ( '</ol>' ) ;
167- res . end ( ) ;
168223 } ) ;
224+ res . write ( '</ol>' ) ;
225+ res . end ( ) ;
169226} ;
170227
171228// Must be last,
172- main ( ) ;
229+ main ( process . argv ) ;
0 commit comments