webrick 1.3.1 → 1.4.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of webrick might be problematic. Click here for more details.

Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/webrick.rb +6 -6
  3. data/lib/webrick/accesslog.rb +9 -1
  4. data/lib/webrick/cgi.rb +51 -2
  5. data/lib/webrick/compat.rb +2 -1
  6. data/lib/webrick/config.rb +42 -5
  7. data/lib/webrick/cookie.rb +68 -6
  8. data/lib/webrick/htmlutils.rb +4 -2
  9. data/lib/webrick/httpauth.rb +1 -0
  10. data/lib/webrick/httpauth/authenticator.rb +13 -8
  11. data/lib/webrick/httpauth/basicauth.rb +3 -3
  12. data/lib/webrick/httpauth/digestauth.rb +25 -9
  13. data/lib/webrick/httpauth/htdigest.rb +8 -4
  14. data/lib/webrick/httpauth/htgroup.rb +1 -0
  15. data/lib/webrick/httpauth/htpasswd.rb +7 -3
  16. data/lib/webrick/httpauth/userdb.rb +1 -0
  17. data/lib/webrick/httpproxy.rb +47 -14
  18. data/lib/webrick/httprequest.rb +142 -16
  19. data/lib/webrick/httpresponse.rb +96 -24
  20. data/lib/webrick/https.rb +24 -1
  21. data/lib/webrick/httpserver.rb +20 -4
  22. data/lib/webrick/httpservlet.rb +1 -0
  23. data/lib/webrick/httpservlet/abstract.rb +2 -1
  24. data/lib/webrick/httpservlet/cgi_runner.rb +1 -0
  25. data/lib/webrick/httpservlet/cgihandler.rb +19 -5
  26. data/lib/webrick/httpservlet/erbhandler.rb +2 -1
  27. data/lib/webrick/httpservlet/filehandler.rb +87 -34
  28. data/lib/webrick/httpservlet/prochandler.rb +14 -0
  29. data/lib/webrick/httpstatus.rb +24 -10
  30. data/lib/webrick/httputils.rb +129 -13
  31. data/lib/webrick/httpversion.rb +28 -1
  32. data/lib/webrick/log.rb +22 -2
  33. data/lib/webrick/server.rb +203 -60
  34. data/lib/webrick/ssl.rb +80 -5
  35. data/lib/webrick/utils.rb +97 -67
  36. data/lib/webrick/version.rb +6 -1
  37. metadata +59 -69
  38. data/README.txt +0 -21
  39. data/sample/webrick/demo-app.rb +0 -66
  40. data/sample/webrick/demo-multipart.cgi +0 -12
  41. data/sample/webrick/demo-servlet.rb +0 -6
  42. data/sample/webrick/demo-urlencoded.cgi +0 -12
  43. data/sample/webrick/hello.cgi +0 -11
  44. data/sample/webrick/hello.rb +0 -8
  45. data/sample/webrick/httpd.rb +0 -23
  46. data/sample/webrick/httpproxy.rb +0 -25
  47. data/sample/webrick/httpsd.rb +0 -33
  48. data/test/openssl/utils.rb +0 -313
  49. data/test/ruby/envutil.rb +0 -208
  50. data/test/webrick/test_cgi.rb +0 -134
  51. data/test/webrick/test_cookie.rb +0 -131
  52. data/test/webrick/test_filehandler.rb +0 -285
  53. data/test/webrick/test_httpauth.rb +0 -167
  54. data/test/webrick/test_httpproxy.rb +0 -282
  55. data/test/webrick/test_httprequest.rb +0 -411
  56. data/test/webrick/test_httpresponse.rb +0 -49
  57. data/test/webrick/test_httpserver.rb +0 -305
  58. data/test/webrick/test_httputils.rb +0 -96
  59. data/test/webrick/test_httpversion.rb +0 -40
  60. data/test/webrick/test_server.rb +0 -67
  61. data/test/webrick/test_utils.rb +0 -64
  62. data/test/webrick/utils.rb +0 -58
  63. data/test/webrick/webrick.cgi +0 -36
  64. data/test/webrick/webrick_long_filename.cgi +0 -36
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpresponse.rb -- HTTPResponse Class
3
4
  #
@@ -16,25 +17,81 @@ require 'webrick/httpstatus'
16
17
 
17
18
  module WEBrick
18
19
  ##
19
- # An HTTP response.
20
+ # An HTTP response. This is filled in by the service or do_* methods of a
21
+ # WEBrick HTTP Servlet.
20
22
 
21
23
  class HTTPResponse
22
- attr_reader :http_version, :status, :header
24
+
25
+ ##
26
+ # HTTP Response version
27
+
28
+ attr_reader :http_version
29
+
30
+ ##
31
+ # Response status code (200)
32
+
33
+ attr_reader :status
34
+
35
+ ##
36
+ # Response header
37
+
38
+ attr_reader :header
39
+
40
+ ##
41
+ # Response cookies
42
+
23
43
  attr_reader :cookies
44
+
45
+ ##
46
+ # Response reason phrase ("OK")
47
+
24
48
  attr_accessor :reason_phrase
25
49
 
26
50
  ##
27
- # Body may be a String or IO subclass.
51
+ # Body may be a String or IO-like object that responds to #read and
52
+ # #readpartial.
28
53
 
29
54
  attr_accessor :body
30
55
 
31
- attr_accessor :request_method, :request_uri, :request_http_version
56
+ ##
57
+ # Request method for this response
58
+
59
+ attr_accessor :request_method
60
+
61
+ ##
62
+ # Request URI for this response
63
+
64
+ attr_accessor :request_uri
65
+
66
+ ##
67
+ # Request HTTP version for this response
68
+
69
+ attr_accessor :request_http_version
70
+
71
+ ##
72
+ # Filename of the static file in this response. Only used by the
73
+ # FileHandler servlet.
74
+
32
75
  attr_accessor :filename
76
+
77
+ ##
78
+ # Is this a keep-alive response?
79
+
33
80
  attr_accessor :keep_alive
34
- attr_reader :config, :sent_size
35
81
 
36
82
  ##
37
- # Creates a new HTTP response object
83
+ # Configuration for this response
84
+
85
+ attr_reader :config
86
+
87
+ ##
88
+ # Bytes sent in this response
89
+
90
+ attr_reader :sent_size
91
+
92
+ ##
93
+ # Creates a new HTTP response object. WEBrick::Config::HTTP is the
94
+ # default configuration.
38
95
 
39
96
  def initialize(config)
40
97
  @config = config
@@ -115,7 +172,7 @@ module WEBrick
115
172
  end
116
173
 
117
174
  ##
118
- # Iterates over each header in the resopnse
175
+ # Iterates over each header in the response
119
176
 
120
177
  def each
121
178
  @header.each{|field, value| yield(field, value) }
@@ -145,7 +202,7 @@ module WEBrick
145
202
  ##
146
203
  # Sends the response on +socket+
147
204
 
148
- def send_response(socket)
205
+ def send_response(socket) # :nodoc:
149
206
  begin
150
207
  setup_header()
151
208
  send_header(socket)
@@ -162,7 +219,7 @@ module WEBrick
162
219
  ##
163
220
  # Sets up the headers for sending
164
221
 
165
- def setup_header()
222
+ def setup_header() # :nodoc:
166
223
  @reason_phrase ||= HTTPStatus::reason_phrase(@status)
167
224
  @header['server'] ||= @config[:ServerSoftware]
168
225
  @header['date'] ||= Time.now.httpdate
@@ -225,7 +282,7 @@ module WEBrick
225
282
  ##
226
283
  # Sends the headers on +socket+
227
284
 
228
- def send_header(socket)
285
+ def send_header(socket) # :nodoc:
229
286
  if @http_version.major > 0
230
287
  data = status_line()
231
288
  @header.each{|key, value|
@@ -243,10 +300,11 @@ module WEBrick
243
300
  ##
244
301
  # Sends the body on +socket+
245
302
 
246
- def send_body(socket)
247
- case @body
248
- when IO then send_body_io(socket)
249
- else send_body_string(socket)
303
+ def send_body(socket) # :nodoc:
304
+ if @body.respond_to? :readpartial then
305
+ send_body_io(socket)
306
+ else
307
+ send_body_string(socket)
250
308
  end
251
309
  end
252
310
 
@@ -264,7 +322,7 @@ module WEBrick
264
322
  # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
265
323
 
266
324
  def set_redirect(status, url)
267
- @body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
325
+ @body = "<HTML><A HREF=\"#{url}\">#{url}</A>.</HTML>\n"
268
326
  @header['location'] = url.to_s
269
327
  raise status
270
328
  end
@@ -294,6 +352,14 @@ module WEBrick
294
352
  host, port = @config[:ServerName], @config[:Port]
295
353
  end
296
354
 
355
+ error_body(backtrace, ex, host, port)
356
+ end
357
+
358
+ private
359
+
360
+ # :stopdoc:
361
+
362
+ def error_body(backtrace, ex, host, port)
297
363
  @body = ''
298
364
  @body << <<-_end_of_html_
299
365
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
@@ -323,20 +389,23 @@ module WEBrick
323
389
  _end_of_html_
324
390
  end
325
391
 
326
- private
327
-
328
392
  def send_body_io(socket)
329
393
  begin
330
394
  if @request_method == "HEAD"
331
395
  # do nothing
332
396
  elsif chunked?
333
- while buf = @body.read(@buffer_size)
334
- next if buf.empty?
335
- data = ""
336
- data << format("%x", buf.bytesize) << CRLF
337
- data << buf << CRLF
338
- _write_data(socket, data)
339
- @sent_size += buf.bytesize
397
+ begin
398
+ buf = ''
399
+ data = ''
400
+ while true
401
+ @body.readpartial( @buffer_size, buf ) # there is no need to clear buf?
402
+ data << format("%x", buf.bytesize) << CRLF
403
+ data << buf << CRLF
404
+ _write_data(socket, data)
405
+ data.clear
406
+ @sent_size += buf.bytesize
407
+ end
408
+ rescue EOFError # do nothing
340
409
  end
341
410
  _write_data(socket, "0#{CRLF}#{CRLF}")
342
411
  else
@@ -395,5 +464,8 @@ module WEBrick
395
464
  def _write_data(socket, data)
396
465
  socket << data
397
466
  end
467
+
468
+ # :startdoc:
398
469
  end
470
+
399
471
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # https.rb -- SSL/TLS enhancement for HTTPServer
3
4
  #
@@ -15,8 +16,28 @@ module WEBrick
15
16
  HTTP.update(SSL)
16
17
  end
17
18
 
19
+ ##
20
+ #--
21
+ # Adds SSL functionality to WEBrick::HTTPRequest
22
+
18
23
  class HTTPRequest
19
- attr_reader :cipher, :server_cert, :client_cert
24
+
25
+ ##
26
+ # HTTP request SSL cipher
27
+
28
+ attr_reader :cipher
29
+
30
+ ##
31
+ # HTTP request server certificate
32
+
33
+ attr_reader :server_cert
34
+
35
+ ##
36
+ # HTTP request client certificate
37
+
38
+ attr_reader :client_cert
39
+
40
+ # :stopdoc:
20
41
 
21
42
  alias orig_parse parse
22
43
 
@@ -60,5 +81,7 @@ module WEBrick
60
81
  end
61
82
  meta
62
83
  end
84
+
85
+ # :startdoc:
63
86
  end
64
87
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpserver.rb -- HTTPServer Class
3
4
  #
@@ -8,6 +9,7 @@
8
9
  #
9
10
  # $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
10
11
 
12
+ require 'io/wait'
11
13
  require 'webrick/server'
12
14
  require 'webrick/httputils'
13
15
  require 'webrick/httpstatus'
@@ -72,11 +74,11 @@ module WEBrick
72
74
  begin
73
75
  timeout = @config[:RequestTimeout]
74
76
  while timeout > 0
75
- break if IO.select([sock], nil, nil, 0.5)
76
- timeout = 0 if @status != :Running
77
+ break if sock.to_io.wait_readable(0.5)
78
+ break if @status != :Running
77
79
  timeout -= 0.5
78
80
  end
79
- raise HTTPStatus::EOFError if timeout <= 0
81
+ raise HTTPStatus::EOFError if timeout <= 0 || @status != :Running
80
82
  raise HTTPStatus::EOFError if sock.eof?
81
83
  req.parse(sock)
82
84
  res.request_method = req.request_method
@@ -138,6 +140,10 @@ module WEBrick
138
140
  si.service(req, res)
139
141
  end
140
142
 
143
+ ##
144
+ # The default OPTIONS request handler says GET, HEAD, POST and OPTIONS
145
+ # requests are allowed.
146
+
141
147
  def do_OPTIONS(req, res)
142
148
  res["allow"] = "GET,HEAD,POST,OPTIONS"
143
149
  end
@@ -207,6 +213,10 @@ module WEBrick
207
213
  }
208
214
  end
209
215
 
216
+ ##
217
+ # Logs +req+ and +res+ in the access logs. +config+ is used for the
218
+ # server name.
219
+
210
220
  def access_log(config, req, res)
211
221
  param = AccessLog::setup_params(config, req, res)
212
222
  @config[:AccessLog].each{|logger, fmt|
@@ -214,7 +224,13 @@ module WEBrick
214
224
  }
215
225
  end
216
226
 
217
- class MountTable
227
+ ##
228
+ # Mount table for the path a servlet is mounted on in the directory space
229
+ # of the server. Users of WEBrick can only access this indirectly via
230
+ # WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and
231
+ # WEBrick::HTTPServer#search_servlet
232
+
233
+ class MountTable # :nodoc:
218
234
  def initialize
219
235
  @tab = Hash.new
220
236
  compile
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpservlet.rb -- HTTPServlet Utility File
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpservlet.rb -- HTTPServlet Module
3
4
  #
@@ -54,7 +55,7 @@ module WEBrick
54
55
  # Servlets can be configured via initialize. The first argument is the
55
56
  # HTTP server the servlet is being initialized for.
56
57
  #
57
- # class Configureable < Simple
58
+ # class Configurable < Simple
58
59
  # def initialize server, color, size
59
60
  # super server
60
61
  # @color = color
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # cgi_runner.rb -- CGI launcher.
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # cgihandler.rb -- CGIHandler Class
3
4
  #
@@ -16,9 +17,20 @@ require 'webrick/httpservlet/abstract'
16
17
  module WEBrick
17
18
  module HTTPServlet
18
19
 
20
+ ##
21
+ # Servlet for handling CGI scripts
22
+ #
23
+ # Example:
24
+ #
25
+ # server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler,
26
+ # '/path/to/my_script')
27
+
19
28
  class CGIHandler < AbstractServlet
20
- Ruby = RbConfig.ruby
21
- CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\""
29
+ Ruby = RbConfig.ruby # :nodoc:
30
+ CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
31
+
32
+ ##
33
+ # Creates a new CGI script servlet for the script at +name+
22
34
 
23
35
  def initialize(server, name)
24
36
  super(server, name)
@@ -27,10 +39,9 @@ module WEBrick
27
39
  @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
28
40
  end
29
41
 
30
- def do_GET(req, res)
31
- data = nil
32
- status = -1
42
+ # :stopdoc:
33
43
 
44
+ def do_GET(req, res)
34
45
  cgi_in = IO::popen(@cgicmd, "wb")
35
46
  cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
36
47
  cgi_out.set_encoding("ASCII-8BIT")
@@ -41,6 +52,7 @@ module WEBrick
41
52
  meta = req.meta_vars
42
53
  meta["SCRIPT_FILENAME"] = @script_filename
43
54
  meta["PATH"] = @config[:CGIPathEnv]
55
+ meta.delete("HTTP_PROXY")
44
56
  if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
45
57
  meta["SystemRoot"] = ENV["SystemRoot"]
46
58
  end
@@ -102,6 +114,8 @@ module WEBrick
102
114
  res.body = body
103
115
  end
104
116
  alias do_POST do_GET
117
+
118
+ # :startdoc:
105
119
  end
106
120
 
107
121
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # erbhandler.rb -- ERBHandler Class
3
4
  #
@@ -56,7 +57,7 @@ module WEBrick
56
57
  res.body = evaluate(ERB.new(data), req, res)
57
58
  res['content-type'] ||=
58
59
  HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
59
- rescue StandardError => ex
60
+ rescue StandardError
60
61
  raise
61
62
  rescue Exception => ex
62
63
  @logger.error(ex)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # filehandler.rb -- FileHandler Module
3
4
  #
@@ -18,12 +19,29 @@ require 'webrick/httpstatus'
18
19
  module WEBrick
19
20
  module HTTPServlet
20
21
 
22
+ ##
23
+ # Servlet for serving a single file. You probably want to use the
24
+ # FileHandler servlet instead as it handles directories and fancy indexes.
25
+ #
26
+ # Example:
27
+ #
28
+ # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
29
+ # '/path/to/my_page.txt')
30
+ #
31
+ # This servlet handles If-Modified-Since and Range requests.
32
+
21
33
  class DefaultFileHandler < AbstractServlet
34
+
35
+ ##
36
+ # Creates a DefaultFileHandler instance for the file at +local_path+.
37
+
22
38
  def initialize(server, local_path)
23
39
  super(server, local_path)
24
40
  @local_path = local_path
25
41
  end
26
42
 
43
+ # :stopdoc:
44
+
27
45
  def do_GET(req, res)
28
46
  st = File::stat(@local_path)
29
47
  mtime = st.mtime
@@ -123,13 +141,21 @@ module WEBrick
123
141
  last = filesize - 1 if last >= filesize
124
142
  return first, last
125
143
  end
144
+
145
+ # :startdoc:
126
146
  end
127
147
 
128
148
  ##
129
- # Serves files from a directory
149
+ # Serves a directory including fancy indexing and a variety of other
150
+ # options.
151
+ #
152
+ # Example:
153
+ #
154
+ # server.mount('/assets', WEBrick::HTTPServlet::FileHandler,
155
+ # '/path/to/assets')
130
156
 
131
157
  class FileHandler < AbstractServlet
132
- HandlerTable = Hash.new
158
+ HandlerTable = Hash.new # :nodoc:
133
159
 
134
160
  ##
135
161
  # Allow custom handling of requests for files with +suffix+ by class
@@ -150,19 +176,8 @@ module WEBrick
150
176
  # Creates a FileHandler servlet on +server+ that serves files starting
151
177
  # at directory +root+
152
178
  #
153
- # If +options+ is a Hash the following keys are allowed:
154
- #
155
- # :AcceptableLanguages:: Array of languages allowed for accept-language
156
- # :DirectoryCallback:: Allows preprocessing of directory requests
157
- # :FancyIndexing:: If true, show an index for directories
158
- # :FileCallback:: Allows preprocessing of file requests
159
- # :HandlerCallback:: Allows preprocessing of requests
160
- # :HandlerTable:: Maps file suffixes to file handlers.
161
- # DefaultFileHandler is used by default but any servlet
162
- # can be used.
163
- # :NondisclosureName:: Do not show files matching this array of globs
164
- # :UserDir:: Directory inside ~user to serve content from for /~user
165
- # requests. Only works if mounted on /
179
+ # +options+ may be a Hash containing keys from
180
+ # WEBrick::Config::FileHandler or +true+ or +false+.
166
181
  #
167
182
  # If +options+ is true or false then +:FancyIndexing+ is enabled or
168
183
  # disabled respectively.
@@ -177,6 +192,8 @@ module WEBrick
177
192
  @options = default.dup.update(options)
178
193
  end
179
194
 
195
+ # :stopdoc:
196
+
180
197
  def service(req, res)
181
198
  # if this class is mounted on "/" and /~username is requested.
182
199
  # we're going to override path informations before invoking service.
@@ -409,11 +426,18 @@ module WEBrick
409
426
  }
410
427
  list.compact!
411
428
 
412
- if d0 = req.query["N"]; idx = 0
413
- elsif d0 = req.query["M"]; idx = 1
414
- elsif d0 = req.query["S"]; idx = 2
415
- else d0 = "A" ; idx = 0
429
+ query = req.query
430
+
431
+ d0 = nil
432
+ idx = nil
433
+ %w[N M S].each_with_index do |q, i|
434
+ if d = query.delete(q)
435
+ idx ||= i
436
+ d0 ||= d
437
+ end
416
438
  end
439
+ d0 ||= "A"
440
+ idx ||= 0
417
441
  d1 = (d0 == "A") ? "D" : "A"
418
442
 
419
443
  if d0 == "A"
@@ -422,38 +446,66 @@ module WEBrick
422
446
  list.sort!{|a,b| b[idx] <=> a[idx] }
423
447
  end
424
448
 
425
- res['content-type'] = "text/html"
449
+ namewidth = query["NameWidth"]
450
+ if namewidth == "*"
451
+ namewidth = nil
452
+ elsif !namewidth or (namewidth = namewidth.to_i) < 2
453
+ namewidth = 25
454
+ end
455
+ query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")}
456
+
457
+ type = "text/html"
458
+ case enc = Encoding.find('filesystem')
459
+ when Encoding::US_ASCII, Encoding::ASCII_8BIT
460
+ else
461
+ type << "; charset=\"#{enc.name}\""
462
+ end
463
+ res['content-type'] = type
426
464
 
465
+ title = "Index of #{HTMLUtils::escape(req.path)}"
427
466
  res.body = <<-_end_of_html_
428
467
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
429
468
  <HTML>
430
- <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD>
469
+ <HEAD>
470
+ <TITLE>#{title}</TITLE>
471
+ <style type="text/css">
472
+ <!--
473
+ .name, .mtime { text-align: left; }
474
+ .size { text-align: right; }
475
+ td { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
476
+ table { border-collapse: collapse; }
477
+ tr th { border-bottom: 2px groove; }
478
+ //-->
479
+ </style>
480
+ </HEAD>
431
481
  <BODY>
432
- <H1>Index of #{HTMLUtils::escape(req.path)}</H1>
482
+ <H1>#{title}</H1>
433
483
  _end_of_html_
434
484
 
435
- res.body << "<PRE>\n"
436
- res.body << " <A HREF=\"?N=#{d1}\">Name</A> "
437
- res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
438
- res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
439
- res.body << "<HR>\n"
485
+ res.body << "<TABLE width=\"100%\"><THEAD><TR>\n"
486
+ res.body << "<TH class=\"name\"><A HREF=\"?N=#{d1}#{query}\">Name</A></TH>"
487
+ res.body << "<TH class=\"mtime\"><A HREF=\"?M=#{d1}#{query}\">Last modified</A></TH>"
488
+ res.body << "<TH class=\"size\"><A HREF=\"?S=#{d1}#{query}\">Size</A></TH>\n"
489
+ res.body << "</TR></THEAD>\n"
490
+ res.body << "<TBODY>\n"
440
491
 
492
+ query.sub!(/\A&/, '?')
441
493
  list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
442
494
  list.each{ |name, time, size|
443
495
  if name == ".."
444
496
  dname = "Parent Directory"
445
- elsif name.bytesize > 25
446
- dname = name.sub(/^(.{23})(?:.*)/, '\1..')
497
+ elsif namewidth and name.size > namewidth
498
+ dname = name[0...(namewidth - 2)] << '..'
447
499
  else
448
500
  dname = name
449
501
  end
450
- s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{HTMLUtils::escape(dname)}</A>"
451
- s << " " * (30 - dname.bytesize)
452
- s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
453
- s << (size >= 0 ? size.to_s : "-") << "\n"
502
+ s = "<TR><TD class=\"name\"><A HREF=\"#{HTTPUtils::escape(name)}#{query if name.end_with?('/')}\">#{HTMLUtils::escape(dname)}</A></TD>"
503
+ s << "<TD class=\"mtime\">" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << "</TD>"
504
+ s << "<TD class=\"size\">" << (size >= 0 ? size.to_s : "-") << "</TD></TR>\n"
454
505
  res.body << s
455
506
  }
456
- res.body << "</PRE><HR>"
507
+ res.body << "</TBODY></TABLE>"
508
+ res.body << "<HR>"
457
509
 
458
510
  res.body << <<-_end_of_html_
459
511
  <ADDRESS>
@@ -465,6 +517,7 @@ module WEBrick
465
517
  _end_of_html_
466
518
  end
467
519
 
520
+ # :startdoc:
468
521
  end
469
522
  end
470
523
  end