webrick 1.3.1 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.

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 +58 -5
  5. data/lib/webrick/compat.rb +2 -1
  6. data/lib/webrick/config.rb +47 -10
  7. data/lib/webrick/cookie.rb +69 -7
  8. data/lib/webrick/htmlutils.rb +4 -2
  9. data/lib/webrick/httpauth.rb +6 -5
  10. data/lib/webrick/httpauth/authenticator.rb +13 -8
  11. data/lib/webrick/httpauth/basicauth.rb +16 -8
  12. data/lib/webrick/httpauth/digestauth.rb +35 -32
  13. data/lib/webrick/httpauth/htdigest.rb +12 -8
  14. data/lib/webrick/httpauth/htgroup.rb +10 -6
  15. data/lib/webrick/httpauth/htpasswd.rb +46 -9
  16. data/lib/webrick/httpauth/userdb.rb +1 -0
  17. data/lib/webrick/httpproxy.rb +93 -48
  18. data/lib/webrick/httprequest.rb +192 -27
  19. data/lib/webrick/httpresponse.rb +182 -62
  20. data/lib/webrick/https.rb +90 -2
  21. data/lib/webrick/httpserver.rb +45 -15
  22. data/lib/webrick/httpservlet.rb +6 -5
  23. data/lib/webrick/httpservlet/abstract.rb +5 -6
  24. data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
  25. data/lib/webrick/httpservlet/cgihandler.rb +22 -10
  26. data/lib/webrick/httpservlet/erbhandler.rb +4 -3
  27. data/lib/webrick/httpservlet/filehandler.rb +136 -65
  28. data/lib/webrick/httpservlet/prochandler.rb +15 -1
  29. data/lib/webrick/httpstatus.rb +24 -14
  30. data/lib/webrick/httputils.rb +132 -13
  31. data/lib/webrick/httpversion.rb +28 -1
  32. data/lib/webrick/log.rb +25 -5
  33. data/lib/webrick/server.rb +234 -74
  34. data/lib/webrick/ssl.rb +100 -12
  35. data/lib/webrick/utils.rb +98 -69
  36. data/lib/webrick/version.rb +6 -1
  37. metadata +66 -72
  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
  # httpserver.rb -- HTTPServer Class
3
4
  #
@@ -8,13 +9,14 @@
8
9
  #
9
10
  # $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
10
11
 
11
- require 'webrick/server'
12
- require 'webrick/httputils'
13
- require 'webrick/httpstatus'
14
- require 'webrick/httprequest'
15
- require 'webrick/httpresponse'
16
- require 'webrick/httpservlet'
17
- require 'webrick/accesslog'
12
+ require 'io/wait'
13
+ require_relative 'server'
14
+ require_relative 'httputils'
15
+ require_relative 'httpstatus'
16
+ require_relative 'httprequest'
17
+ require_relative 'httpresponse'
18
+ require_relative 'httpservlet'
19
+ require_relative 'accesslog'
18
20
 
19
21
  module WEBrick
20
22
  class HTTPServerError < ServerError; end
@@ -66,17 +68,17 @@ module WEBrick
66
68
 
67
69
  def run(sock)
68
70
  while true
69
- res = HTTPResponse.new(@config)
70
- req = HTTPRequest.new(@config)
71
+ req = create_request(@config)
72
+ res = create_response(@config)
71
73
  server = self
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,27 @@ module WEBrick
214
224
  }
215
225
  end
216
226
 
217
- class MountTable
227
+ ##
228
+ # Creates the HTTPRequest used when handling the HTTP
229
+ # request. Can be overridden by subclasses.
230
+ def create_request(with_webrick_config)
231
+ HTTPRequest.new(with_webrick_config)
232
+ end
233
+
234
+ ##
235
+ # Creates the HTTPResponse used when handling the HTTP
236
+ # request. Can be overridden by subclasses.
237
+ def create_response(with_webrick_config)
238
+ HTTPResponse.new(with_webrick_config)
239
+ end
240
+
241
+ ##
242
+ # Mount table for the path a servlet is mounted on in the directory space
243
+ # of the server. Users of WEBrick can only access this indirectly via
244
+ # WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and
245
+ # WEBrick::HTTPServer#search_servlet
246
+
247
+ class MountTable # :nodoc:
218
248
  def initialize
219
249
  @tab = Hash.new
220
250
  compile
@@ -251,12 +281,12 @@ module WEBrick
251
281
  k.sort!
252
282
  k.reverse!
253
283
  k.collect!{|path| Regexp.escape(path) }
254
- @scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)")
284
+ @scanner = Regexp.new("\\A(" + k.join("|") +")(?=/|\\z)")
255
285
  end
256
286
 
257
287
  def normalize(dir)
258
288
  ret = dir ? dir.dup : ""
259
- ret.sub!(%r|/+$|, "")
289
+ ret.sub!(%r|/+\z|, "")
260
290
  ret
261
291
  end
262
292
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpservlet.rb -- HTTPServlet Utility File
3
4
  #
@@ -8,11 +9,11 @@
8
9
  #
9
10
  # $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $
10
11
 
11
- require 'webrick/httpservlet/abstract'
12
- require 'webrick/httpservlet/filehandler'
13
- require 'webrick/httpservlet/cgihandler'
14
- require 'webrick/httpservlet/erbhandler'
15
- require 'webrick/httpservlet/prochandler'
12
+ require_relative 'httpservlet/abstract'
13
+ require_relative 'httpservlet/filehandler'
14
+ require_relative 'httpservlet/cgihandler'
15
+ require_relative 'httpservlet/erbhandler'
16
+ require_relative 'httpservlet/prochandler'
16
17
 
17
18
  module WEBrick
18
19
  module HTTPServlet
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpservlet.rb -- HTTPServlet Module
3
4
  #
@@ -8,11 +9,9 @@
8
9
  #
9
10
  # $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $
10
11
 
11
- require 'thread'
12
-
13
- require 'webrick/htmlutils'
14
- require 'webrick/httputils'
15
- require 'webrick/httpstatus'
12
+ require_relative '../htmlutils'
13
+ require_relative '../httputils'
14
+ require_relative '../httpstatus'
16
15
 
17
16
  module WEBrick
18
17
  module HTTPServlet
@@ -54,7 +53,7 @@ module WEBrick
54
53
  # Servlets can be configured via initialize. The first argument is the
55
54
  # HTTP server the servlet is being initialized for.
56
55
  #
57
- # class Configureable < Simple
56
+ # class Configurable < Simple
58
57
  # def initialize server, color, size
59
58
  # super server
60
59
  # @color = color
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # cgi_runner.rb -- CGI launcher.
3
4
  #
@@ -22,11 +23,11 @@ STDIN.binmode
22
23
 
23
24
  len = sysread(STDIN, 8).to_i
24
25
  out = sysread(STDIN, len)
25
- STDOUT.reopen(open(out, "w"))
26
+ STDOUT.reopen(File.open(out, "w"))
26
27
 
27
28
  len = sysread(STDIN, 8).to_i
28
29
  err = sysread(STDIN, len)
29
- STDERR.reopen(open(err, "w"))
30
+ STDERR.reopen(File.open(err, "w"))
30
31
 
31
32
  len = sysread(STDIN, 8).to_i
32
33
  dump = sysread(STDIN, len)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # cgihandler.rb -- CGIHandler Class
3
4
  #
@@ -10,15 +11,26 @@
10
11
 
11
12
  require 'rbconfig'
12
13
  require 'tempfile'
13
- require 'webrick/config'
14
- require 'webrick/httpservlet/abstract'
14
+ require_relative '../config'
15
+ require_relative 'abstract'
15
16
 
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
@@ -53,9 +65,7 @@ module WEBrick
53
65
  cgi_in.write("%8d" % dump.bytesize)
54
66
  cgi_in.write(dump)
55
67
 
56
- if req.body and req.body.bytesize > 0
57
- cgi_in.write(req.body)
58
- end
68
+ req.body { |chunk| cgi_in.write(chunk) }
59
69
  ensure
60
70
  cgi_in.close
61
71
  status = $?.exitstatus
@@ -102,6 +112,8 @@ module WEBrick
102
112
  res.body = body
103
113
  end
104
114
  alias do_POST do_GET
115
+
116
+ # :startdoc:
105
117
  end
106
118
 
107
119
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # erbhandler.rb -- ERBHandler Class
3
4
  #
@@ -8,7 +9,7 @@
8
9
  #
9
10
  # $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
10
11
 
11
- require 'webrick/httpservlet/abstract.rb'
12
+ require_relative 'abstract'
12
13
 
13
14
  require 'erb'
14
15
 
@@ -52,11 +53,11 @@ module WEBrick
52
53
  raise HTTPStatus::Forbidden, "ERBHandler cannot work."
53
54
  end
54
55
  begin
55
- data = open(@script_filename){|io| io.read }
56
+ data = File.open(@script_filename, &:read)
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
  #
@@ -8,22 +9,38 @@
8
9
  #
9
10
  # $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $
10
11
 
11
- require 'thread'
12
12
  require 'time'
13
13
 
14
- require 'webrick/htmlutils'
15
- require 'webrick/httputils'
16
- require 'webrick/httpstatus'
14
+ require_relative '../htmlutils'
15
+ require_relative '../httputils'
16
+ require_relative '../httpstatus'
17
17
 
18
18
  module WEBrick
19
19
  module HTTPServlet
20
20
 
21
+ ##
22
+ # Servlet for serving a single file. You probably want to use the
23
+ # FileHandler servlet instead as it handles directories and fancy indexes.
24
+ #
25
+ # Example:
26
+ #
27
+ # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
28
+ # '/path/to/my_page.txt')
29
+ #
30
+ # This servlet handles If-Modified-Since and Range requests.
31
+
21
32
  class DefaultFileHandler < AbstractServlet
33
+
34
+ ##
35
+ # Creates a DefaultFileHandler instance for the file at +local_path+.
36
+
22
37
  def initialize(server, local_path)
23
38
  super(server, local_path)
24
39
  @local_path = local_path
25
40
  end
26
41
 
42
+ # :stopdoc:
43
+
27
44
  def do_GET(req, res)
28
45
  st = File::stat(@local_path)
29
46
  mtime = st.mtime
@@ -38,9 +55,9 @@ module WEBrick
38
55
  else
39
56
  mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
40
57
  res['content-type'] = mtype
41
- res['content-length'] = st.size
58
+ res['content-length'] = st.size.to_s
42
59
  res['last-modified'] = mtime.httpdate
43
- res.body = open(@local_path, "rb")
60
+ res.body = File.open(@local_path, "rb")
44
61
  end
45
62
  end
46
63
 
@@ -69,47 +86,66 @@ module WEBrick
69
86
  return false
70
87
  end
71
88
 
89
+ # returns a lambda for webrick/httpresponse.rb send_body_proc
90
+ def multipart_body(body, parts, boundary, mtype, filesize)
91
+ lambda do |socket|
92
+ begin
93
+ begin
94
+ first = parts.shift
95
+ last = parts.shift
96
+ socket.write(
97
+ "--#{boundary}#{CRLF}" \
98
+ "Content-Type: #{mtype}#{CRLF}" \
99
+ "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \
100
+ "#{CRLF}"
101
+ )
102
+
103
+ begin
104
+ IO.copy_stream(body, socket, last - first + 1, first)
105
+ rescue NotImplementedError
106
+ body.seek(first, IO::SEEK_SET)
107
+ IO.copy_stream(body, socket, last - first + 1)
108
+ end
109
+ socket.write(CRLF)
110
+ end while parts[0]
111
+ socket.write("--#{boundary}--#{CRLF}")
112
+ ensure
113
+ body.close
114
+ end
115
+ end
116
+ end
117
+
72
118
  def make_partial_content(req, res, filename, filesize)
73
119
  mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
74
120
  unless ranges = HTTPUtils::parse_range_header(req['range'])
75
121
  raise HTTPStatus::BadRequest,
76
122
  "Unrecognized range-spec: \"#{req['range']}\""
77
123
  end
78
- open(filename, "rb"){|io|
124
+ File.open(filename, "rb"){|io|
79
125
  if ranges.size > 1
80
126
  time = Time.now
81
127
  boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
82
- body = ''
83
- ranges.each{|range|
84
- first, last = prepare_range(range, filesize)
85
- next if first < 0
86
- io.pos = first
87
- content = io.read(last-first+1)
88
- body << "--" << boundary << CRLF
89
- body << "Content-Type: #{mtype}" << CRLF
90
- body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
91
- body << CRLF
92
- body << content
93
- body << CRLF
128
+ parts = []
129
+ ranges.each {|range|
130
+ prange = prepare_range(range, filesize)
131
+ next if prange[0] < 0
132
+ parts.concat(prange)
94
133
  }
95
- raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
96
- body << "--" << boundary << "--" << CRLF
134
+ raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty?
97
135
  res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
98
- res.body = body
136
+ if req.http_version < '1.1'
137
+ res['connection'] = 'close'
138
+ else
139
+ res.chunked = true
140
+ end
141
+ res.body = multipart_body(io.dup, parts, boundary, mtype, filesize)
99
142
  elsif range = ranges[0]
100
143
  first, last = prepare_range(range, filesize)
101
144
  raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
102
- if last == filesize - 1
103
- content = io.dup
104
- content.pos = first
105
- else
106
- io.pos = first
107
- content = io.read(last-first+1)
108
- end
109
145
  res['content-type'] = mtype
110
146
  res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
111
- res['content-length'] = last - first + 1
112
- res.body = content
147
+ res['content-length'] = (last - first + 1).to_s
148
+ res.body = io.dup
113
149
  else
114
150
  raise HTTPStatus::BadRequest
115
151
  end
@@ -123,13 +159,21 @@ module WEBrick
123
159
  last = filesize - 1 if last >= filesize
124
160
  return first, last
125
161
  end
162
+
163
+ # :startdoc:
126
164
  end
127
165
 
128
166
  ##
129
- # Serves files from a directory
167
+ # Serves a directory including fancy indexing and a variety of other
168
+ # options.
169
+ #
170
+ # Example:
171
+ #
172
+ # server.mount('/assets', WEBrick::HTTPServlet::FileHandler,
173
+ # '/path/to/assets')
130
174
 
131
175
  class FileHandler < AbstractServlet
132
- HandlerTable = Hash.new
176
+ HandlerTable = Hash.new # :nodoc:
133
177
 
134
178
  ##
135
179
  # Allow custom handling of requests for files with +suffix+ by class
@@ -150,19 +194,8 @@ module WEBrick
150
194
  # Creates a FileHandler servlet on +server+ that serves files starting
151
195
  # at directory +root+
152
196
  #
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 /
197
+ # +options+ may be a Hash containing keys from
198
+ # WEBrick::Config::FileHandler or +true+ or +false+.
166
199
  #
167
200
  # If +options+ is true or false then +:FancyIndexing+ is enabled or
168
201
  # disabled respectively.
@@ -177,6 +210,8 @@ module WEBrick
177
210
  @options = default.dup.update(options)
178
211
  end
179
212
 
213
+ # :stopdoc:
214
+
180
215
  def service(req, res)
181
216
  # if this class is mounted on "/" and /~username is requested.
182
217
  # we're going to override path informations before invoking service.
@@ -409,11 +444,18 @@ module WEBrick
409
444
  }
410
445
  list.compact!
411
446
 
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
447
+ query = req.query
448
+
449
+ d0 = nil
450
+ idx = nil
451
+ %w[N M S].each_with_index do |q, i|
452
+ if d = query.delete(q)
453
+ idx ||= i
454
+ d0 ||= d
455
+ end
416
456
  end
457
+ d0 ||= "A"
458
+ idx ||= 0
417
459
  d1 = (d0 == "A") ? "D" : "A"
418
460
 
419
461
  if d0 == "A"
@@ -422,38 +464,66 @@ module WEBrick
422
464
  list.sort!{|a,b| b[idx] <=> a[idx] }
423
465
  end
424
466
 
425
- res['content-type'] = "text/html"
467
+ namewidth = query["NameWidth"]
468
+ if namewidth == "*"
469
+ namewidth = nil
470
+ elsif !namewidth or (namewidth = namewidth.to_i) < 2
471
+ namewidth = 25
472
+ end
473
+ query = query.inject('') {|s, (k, v)| s << '&' << HTMLUtils::escape("#{k}=#{v}")}
474
+
475
+ type = "text/html"
476
+ case enc = Encoding.find('filesystem')
477
+ when Encoding::US_ASCII, Encoding::ASCII_8BIT
478
+ else
479
+ type << "; charset=\"#{enc.name}\""
480
+ end
481
+ res['content-type'] = type
426
482
 
483
+ title = "Index of #{HTMLUtils::escape(req.path)}"
427
484
  res.body = <<-_end_of_html_
428
485
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
429
486
  <HTML>
430
- <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD>
487
+ <HEAD>
488
+ <TITLE>#{title}</TITLE>
489
+ <style type="text/css">
490
+ <!--
491
+ .name, .mtime { text-align: left; }
492
+ .size { text-align: right; }
493
+ td { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }
494
+ table { border-collapse: collapse; }
495
+ tr th { border-bottom: 2px groove; }
496
+ //-->
497
+ </style>
498
+ </HEAD>
431
499
  <BODY>
432
- <H1>Index of #{HTMLUtils::escape(req.path)}</H1>
500
+ <H1>#{title}</H1>
433
501
  _end_of_html_
434
502
 
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"
503
+ res.body << "<TABLE width=\"100%\"><THEAD><TR>\n"
504
+ res.body << "<TH class=\"name\"><A HREF=\"?N=#{d1}#{query}\">Name</A></TH>"
505
+ res.body << "<TH class=\"mtime\"><A HREF=\"?M=#{d1}#{query}\">Last modified</A></TH>"
506
+ res.body << "<TH class=\"size\"><A HREF=\"?S=#{d1}#{query}\">Size</A></TH>\n"
507
+ res.body << "</TR></THEAD>\n"
508
+ res.body << "<TBODY>\n"
440
509
 
510
+ query.sub!(/\A&/, '?')
441
511
  list.unshift [ "..", File::mtime(local_path+"/.."), -1 ]
442
512
  list.each{ |name, time, size|
443
513
  if name == ".."
444
514
  dname = "Parent Directory"
445
- elsif name.bytesize > 25
446
- dname = name.sub(/^(.{23})(?:.*)/, '\1..')
515
+ elsif namewidth and name.size > namewidth
516
+ dname = name[0...(namewidth - 2)] << '..'
447
517
  else
448
518
  dname = name
449
519
  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"
520
+ s = "<TR><TD class=\"name\"><A HREF=\"#{HTTPUtils::escape(name)}#{query if name.end_with?('/')}\">#{HTMLUtils::escape(dname)}</A></TD>"
521
+ s << "<TD class=\"mtime\">" << (time ? time.strftime("%Y/%m/%d %H:%M") : "") << "</TD>"
522
+ s << "<TD class=\"size\">" << (size >= 0 ? size.to_s : "-") << "</TD></TR>\n"
454
523
  res.body << s
455
524
  }
456
- res.body << "</PRE><HR>"
525
+ res.body << "</TBODY></TABLE>"
526
+ res.body << "<HR>"
457
527
 
458
528
  res.body << <<-_end_of_html_
459
529
  <ADDRESS>
@@ -465,6 +535,7 @@ module WEBrick
465
535
  _end_of_html_
466
536
  end
467
537
 
538
+ # :startdoc:
468
539
  end
469
540
  end
470
541
  end