webrick 1.3.1 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/webrick.rb +7 -7
- data/lib/webrick/accesslog.rb +12 -6
- data/lib/webrick/cgi.rb +58 -5
- data/lib/webrick/compat.rb +2 -1
- data/lib/webrick/config.rb +47 -10
- data/lib/webrick/cookie.rb +69 -7
- data/lib/webrick/htmlutils.rb +4 -2
- data/lib/webrick/httpauth.rb +6 -5
- data/lib/webrick/httpauth/authenticator.rb +13 -8
- data/lib/webrick/httpauth/basicauth.rb +16 -8
- data/lib/webrick/httpauth/digestauth.rb +35 -32
- data/lib/webrick/httpauth/htdigest.rb +12 -8
- data/lib/webrick/httpauth/htgroup.rb +10 -6
- data/lib/webrick/httpauth/htpasswd.rb +46 -9
- data/lib/webrick/httpauth/userdb.rb +1 -0
- data/lib/webrick/httpproxy.rb +93 -48
- data/lib/webrick/httprequest.rb +201 -31
- data/lib/webrick/httpresponse.rb +235 -70
- data/lib/webrick/https.rb +90 -2
- data/lib/webrick/httpserver.rb +45 -15
- data/lib/webrick/httpservlet.rb +6 -5
- data/lib/webrick/httpservlet/abstract.rb +5 -6
- data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
- data/lib/webrick/httpservlet/cgihandler.rb +29 -11
- data/lib/webrick/httpservlet/erbhandler.rb +4 -3
- data/lib/webrick/httpservlet/filehandler.rb +136 -65
- data/lib/webrick/httpservlet/prochandler.rb +15 -1
- data/lib/webrick/httpstatus.rb +24 -14
- data/lib/webrick/httputils.rb +134 -17
- data/lib/webrick/httpversion.rb +28 -1
- data/lib/webrick/log.rb +25 -5
- data/lib/webrick/server.rb +234 -74
- data/lib/webrick/ssl.rb +100 -12
- data/lib/webrick/utils.rb +98 -69
- data/lib/webrick/version.rb +6 -1
- data/webrick.gemspec +76 -0
- metadata +73 -72
- data/README.txt +0 -21
- data/sample/webrick/demo-app.rb +0 -66
- data/sample/webrick/demo-multipart.cgi +0 -12
- data/sample/webrick/demo-servlet.rb +0 -6
- data/sample/webrick/demo-urlencoded.cgi +0 -12
- data/sample/webrick/hello.cgi +0 -11
- data/sample/webrick/hello.rb +0 -8
- data/sample/webrick/httpd.rb +0 -23
- data/sample/webrick/httpproxy.rb +0 -25
- data/sample/webrick/httpsd.rb +0 -33
- data/test/openssl/utils.rb +0 -313
- data/test/ruby/envutil.rb +0 -208
- data/test/webrick/test_cgi.rb +0 -134
- data/test/webrick/test_cookie.rb +0 -131
- data/test/webrick/test_filehandler.rb +0 -285
- data/test/webrick/test_httpauth.rb +0 -167
- data/test/webrick/test_httpproxy.rb +0 -282
- data/test/webrick/test_httprequest.rb +0 -411
- data/test/webrick/test_httpresponse.rb +0 -49
- data/test/webrick/test_httpserver.rb +0 -305
- data/test/webrick/test_httputils.rb +0 -96
- data/test/webrick/test_httpversion.rb +0 -40
- data/test/webrick/test_server.rb +0 -67
- data/test/webrick/test_utils.rb +0 -64
- data/test/webrick/utils.rb +0 -58
- data/test/webrick/webrick.cgi +0 -36
- data/test/webrick/webrick_long_filename.cgi +0 -36
data/lib/webrick/https.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# https.rb -- SSL/TLS enhancement for HTTPServer
|
3
4
|
#
|
@@ -8,15 +9,36 @@
|
|
8
9
|
#
|
9
10
|
# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
|
10
11
|
|
11
|
-
|
12
|
+
require_relative 'ssl'
|
13
|
+
require_relative 'httpserver'
|
12
14
|
|
13
15
|
module WEBrick
|
14
16
|
module Config
|
15
17
|
HTTP.update(SSL)
|
16
18
|
end
|
17
19
|
|
20
|
+
##
|
21
|
+
#--
|
22
|
+
# Adds SSL functionality to WEBrick::HTTPRequest
|
23
|
+
|
18
24
|
class HTTPRequest
|
19
|
-
|
25
|
+
|
26
|
+
##
|
27
|
+
# HTTP request SSL cipher
|
28
|
+
|
29
|
+
attr_reader :cipher
|
30
|
+
|
31
|
+
##
|
32
|
+
# HTTP request server certificate
|
33
|
+
|
34
|
+
attr_reader :server_cert
|
35
|
+
|
36
|
+
##
|
37
|
+
# HTTP request client certificate
|
38
|
+
|
39
|
+
attr_reader :client_cert
|
40
|
+
|
41
|
+
# :stopdoc:
|
20
42
|
|
21
43
|
alias orig_parse parse
|
22
44
|
|
@@ -60,5 +82,71 @@ module WEBrick
|
|
60
82
|
end
|
61
83
|
meta
|
62
84
|
end
|
85
|
+
|
86
|
+
# :startdoc:
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
#--
|
91
|
+
# Fake WEBrick::HTTPRequest for lookup_server
|
92
|
+
|
93
|
+
class SNIRequest
|
94
|
+
|
95
|
+
##
|
96
|
+
# The SNI hostname
|
97
|
+
|
98
|
+
attr_reader :host
|
99
|
+
|
100
|
+
##
|
101
|
+
# The socket address of the server
|
102
|
+
|
103
|
+
attr_reader :addr
|
104
|
+
|
105
|
+
##
|
106
|
+
# The port this request is for
|
107
|
+
|
108
|
+
attr_reader :port
|
109
|
+
|
110
|
+
##
|
111
|
+
# Creates a new SNIRequest.
|
112
|
+
|
113
|
+
def initialize(sslsocket, hostname)
|
114
|
+
@host = hostname
|
115
|
+
@addr = sslsocket.addr
|
116
|
+
@port = @addr[1]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
##
|
122
|
+
#--
|
123
|
+
# Adds SSL functionality to WEBrick::HTTPServer
|
124
|
+
|
125
|
+
class HTTPServer < ::WEBrick::GenericServer
|
126
|
+
##
|
127
|
+
# ServerNameIndication callback
|
128
|
+
|
129
|
+
def ssl_servername_callback(sslsocket, hostname = nil)
|
130
|
+
req = SNIRequest.new(sslsocket, hostname)
|
131
|
+
server = lookup_server(req)
|
132
|
+
server ? server.ssl_context : nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# :stopdoc:
|
136
|
+
|
137
|
+
##
|
138
|
+
# Check whether +server+ is also SSL server.
|
139
|
+
# Also +server+'s SSL context will be created.
|
140
|
+
|
141
|
+
alias orig_virtual_host virtual_host
|
142
|
+
|
143
|
+
def virtual_host(server)
|
144
|
+
if @config[:SSLEnable] && !server.ssl_context
|
145
|
+
raise ArgumentError, "virtual host must set SSLEnable to true"
|
146
|
+
end
|
147
|
+
orig_virtual_host(server)
|
148
|
+
end
|
149
|
+
|
150
|
+
# :startdoc:
|
63
151
|
end
|
64
152
|
end
|
data/lib/webrick/httpserver.rb
CHANGED
@@ -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 '
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
70
|
-
|
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
|
76
|
-
|
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
|
-
|
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("
|
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
|
data/lib/webrick/httpservlet.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
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,27 +11,43 @@
|
|
10
11
|
|
11
12
|
require 'rbconfig'
|
12
13
|
require 'tempfile'
|
13
|
-
|
14
|
-
|
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
|
+
CGIRunnerArray = [Ruby, "#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb".freeze].freeze # :nodoc:
|
32
|
+
|
33
|
+
##
|
34
|
+
# Creates a new CGI script servlet for the script at +name+
|
22
35
|
|
23
36
|
def initialize(server, name)
|
24
37
|
super(server, name)
|
25
38
|
@script_filename = name
|
26
39
|
@tempdir = server[:TempDir]
|
27
|
-
|
40
|
+
interpreter = server[:CGIInterpreter]
|
41
|
+
if interpreter.is_a?(Array)
|
42
|
+
@cgicmd = CGIRunnerArray + interpreter
|
43
|
+
else
|
44
|
+
@cgicmd = "#{CGIRunner} #{interpreter}"
|
45
|
+
end
|
28
46
|
end
|
29
47
|
|
30
|
-
|
31
|
-
data = nil
|
32
|
-
status = -1
|
48
|
+
# :stopdoc:
|
33
49
|
|
50
|
+
def do_GET(req, res)
|
34
51
|
cgi_in = IO::popen(@cgicmd, "wb")
|
35
52
|
cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
|
36
53
|
cgi_out.set_encoding("ASCII-8BIT")
|
@@ -41,6 +58,7 @@ module WEBrick
|
|
41
58
|
meta = req.meta_vars
|
42
59
|
meta["SCRIPT_FILENAME"] = @script_filename
|
43
60
|
meta["PATH"] = @config[:CGIPathEnv]
|
61
|
+
meta.delete("HTTP_PROXY")
|
44
62
|
if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
|
45
63
|
meta["SystemRoot"] = ENV["SystemRoot"]
|
46
64
|
end
|
@@ -53,9 +71,7 @@ module WEBrick
|
|
53
71
|
cgi_in.write("%8d" % dump.bytesize)
|
54
72
|
cgi_in.write(dump)
|
55
73
|
|
56
|
-
|
57
|
-
cgi_in.write(req.body)
|
58
|
-
end
|
74
|
+
req.body { |chunk| cgi_in.write(chunk) }
|
59
75
|
ensure
|
60
76
|
cgi_in.close
|
61
77
|
status = $?.exitstatus
|
@@ -102,6 +118,8 @@ module WEBrick
|
|
102
118
|
res.body = body
|
103
119
|
end
|
104
120
|
alias do_POST do_GET
|
121
|
+
|
122
|
+
# :startdoc:
|
105
123
|
end
|
106
124
|
|
107
125
|
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
|
-
|
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
|
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
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
83
|
-
ranges.each{|range|
|
84
|
-
|
85
|
-
next if
|
86
|
-
|
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
|
96
|
-
body << "--" << boundary << "--" << CRLF
|
134
|
+
raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty?
|
97
135
|
res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
|
98
|
-
|
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 =
|
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
|
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
|
-
#
|
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
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
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
|
-
|
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
|
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
|
500
|
+
<H1>#{title}</H1>
|
433
501
|
_end_of_html_
|
434
502
|
|
435
|
-
res.body << "<
|
436
|
-
res.body << "
|
437
|
-
res.body << "<A HREF=\"?M=#{d1}\">Last modified</A>
|
438
|
-
res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
|
439
|
-
res.body << "
|
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.
|
446
|
-
dname = name
|
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 = "
|
451
|
-
s << " "
|
452
|
-
s << (
|
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 << "</
|
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
|