webrick 1.3.1
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.
- data/README.txt +21 -0
- data/lib/webrick.rb +227 -0
- data/lib/webrick/accesslog.rb +151 -0
- data/lib/webrick/cgi.rb +260 -0
- data/lib/webrick/compat.rb +35 -0
- data/lib/webrick/config.rb +121 -0
- data/lib/webrick/cookie.rb +110 -0
- data/lib/webrick/htmlutils.rb +28 -0
- data/lib/webrick/httpauth.rb +95 -0
- data/lib/webrick/httpauth/authenticator.rb +112 -0
- data/lib/webrick/httpauth/basicauth.rb +108 -0
- data/lib/webrick/httpauth/digestauth.rb +392 -0
- data/lib/webrick/httpauth/htdigest.rb +128 -0
- data/lib/webrick/httpauth/htgroup.rb +93 -0
- data/lib/webrick/httpauth/htpasswd.rb +121 -0
- data/lib/webrick/httpauth/userdb.rb +52 -0
- data/lib/webrick/httpproxy.rb +305 -0
- data/lib/webrick/httprequest.rb +461 -0
- data/lib/webrick/httpresponse.rb +399 -0
- data/lib/webrick/https.rb +64 -0
- data/lib/webrick/httpserver.rb +264 -0
- data/lib/webrick/httpservlet.rb +22 -0
- data/lib/webrick/httpservlet/abstract.rb +153 -0
- data/lib/webrick/httpservlet/cgi_runner.rb +46 -0
- data/lib/webrick/httpservlet/cgihandler.rb +108 -0
- data/lib/webrick/httpservlet/erbhandler.rb +87 -0
- data/lib/webrick/httpservlet/filehandler.rb +470 -0
- data/lib/webrick/httpservlet/prochandler.rb +33 -0
- data/lib/webrick/httpstatus.rb +184 -0
- data/lib/webrick/httputils.rb +394 -0
- data/lib/webrick/httpversion.rb +49 -0
- data/lib/webrick/log.rb +136 -0
- data/lib/webrick/server.rb +218 -0
- data/lib/webrick/ssl.rb +127 -0
- data/lib/webrick/utils.rb +241 -0
- data/lib/webrick/version.rb +13 -0
- data/sample/webrick/demo-app.rb +66 -0
- data/sample/webrick/demo-multipart.cgi +12 -0
- data/sample/webrick/demo-servlet.rb +6 -0
- data/sample/webrick/demo-urlencoded.cgi +12 -0
- data/sample/webrick/hello.cgi +11 -0
- data/sample/webrick/hello.rb +8 -0
- data/sample/webrick/httpd.rb +23 -0
- data/sample/webrick/httpproxy.rb +25 -0
- data/sample/webrick/httpsd.rb +33 -0
- data/test/openssl/utils.rb +313 -0
- data/test/ruby/envutil.rb +208 -0
- data/test/webrick/test_cgi.rb +134 -0
- data/test/webrick/test_cookie.rb +131 -0
- data/test/webrick/test_filehandler.rb +285 -0
- data/test/webrick/test_httpauth.rb +167 -0
- data/test/webrick/test_httpproxy.rb +282 -0
- data/test/webrick/test_httprequest.rb +411 -0
- data/test/webrick/test_httpresponse.rb +49 -0
- data/test/webrick/test_httpserver.rb +305 -0
- data/test/webrick/test_httputils.rb +96 -0
- data/test/webrick/test_httpversion.rb +40 -0
- data/test/webrick/test_server.rb +67 -0
- data/test/webrick/test_utils.rb +64 -0
- data/test/webrick/utils.rb +58 -0
- data/test/webrick/webrick.cgi +36 -0
- data/test/webrick/webrick_long_filename.cgi +36 -0
- metadata +106 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
#
|
2
|
+
# utils.rb -- Miscellaneous utilities
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
5
|
+
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
|
6
|
+
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
|
7
|
+
# reserved.
|
8
|
+
#
|
9
|
+
# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
|
10
|
+
|
11
|
+
require 'socket'
|
12
|
+
require 'fcntl'
|
13
|
+
begin
|
14
|
+
require 'etc'
|
15
|
+
rescue LoadError
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
module WEBrick
|
20
|
+
module Utils
|
21
|
+
##
|
22
|
+
# Sets IO operations on +io+ to be non-blocking
|
23
|
+
def set_non_blocking(io)
|
24
|
+
flag = File::NONBLOCK
|
25
|
+
if defined?(Fcntl::F_GETFL)
|
26
|
+
flag |= io.fcntl(Fcntl::F_GETFL)
|
27
|
+
end
|
28
|
+
io.fcntl(Fcntl::F_SETFL, flag)
|
29
|
+
end
|
30
|
+
module_function :set_non_blocking
|
31
|
+
|
32
|
+
##
|
33
|
+
# Sets the close on exec flag for +io+
|
34
|
+
def set_close_on_exec(io)
|
35
|
+
if defined?(Fcntl::FD_CLOEXEC)
|
36
|
+
io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
module_function :set_close_on_exec
|
40
|
+
|
41
|
+
##
|
42
|
+
# Changes the process's uid and gid to the ones of +user+
|
43
|
+
def su(user)
|
44
|
+
if defined?(Etc)
|
45
|
+
pw = Etc.getpwnam(user)
|
46
|
+
Process::initgroups(user, pw.gid)
|
47
|
+
Process::Sys::setgid(pw.gid)
|
48
|
+
Process::Sys::setuid(pw.uid)
|
49
|
+
else
|
50
|
+
warn("WEBrick::Utils::su doesn't work on this platform")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
module_function :su
|
54
|
+
|
55
|
+
##
|
56
|
+
# The server hostname
|
57
|
+
def getservername
|
58
|
+
host = Socket::gethostname
|
59
|
+
begin
|
60
|
+
Socket::gethostbyname(host)[0]
|
61
|
+
rescue
|
62
|
+
host
|
63
|
+
end
|
64
|
+
end
|
65
|
+
module_function :getservername
|
66
|
+
|
67
|
+
##
|
68
|
+
# Creates TCP server sockets bound to +address+:+port+ and returns them.
|
69
|
+
#
|
70
|
+
# It will create IPV4 and IPV6 sockets on all interfaces.
|
71
|
+
def create_listeners(address, port, logger=nil)
|
72
|
+
unless port
|
73
|
+
raise ArgumentError, "must specify port"
|
74
|
+
end
|
75
|
+
res = Socket::getaddrinfo(address, port,
|
76
|
+
Socket::AF_UNSPEC, # address family
|
77
|
+
Socket::SOCK_STREAM, # socket type
|
78
|
+
0, # protocol
|
79
|
+
Socket::AI_PASSIVE) # flag
|
80
|
+
last_error = nil
|
81
|
+
sockets = []
|
82
|
+
res.each{|ai|
|
83
|
+
begin
|
84
|
+
logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
|
85
|
+
sock = TCPServer.new(ai[3], port)
|
86
|
+
port = sock.addr[1] if port == 0
|
87
|
+
Utils::set_close_on_exec(sock)
|
88
|
+
sockets << sock
|
89
|
+
rescue => ex
|
90
|
+
logger.warn("TCPServer Error: #{ex}") if logger
|
91
|
+
last_error = ex
|
92
|
+
end
|
93
|
+
}
|
94
|
+
raise last_error if sockets.empty?
|
95
|
+
return sockets
|
96
|
+
end
|
97
|
+
module_function :create_listeners
|
98
|
+
|
99
|
+
##
|
100
|
+
# Characters used to generate random strings
|
101
|
+
RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
102
|
+
"0123456789" +
|
103
|
+
"abcdefghijklmnopqrstuvwxyz"
|
104
|
+
|
105
|
+
##
|
106
|
+
# Generates a random string of length +len+
|
107
|
+
def random_string(len)
|
108
|
+
rand_max = RAND_CHARS.bytesize
|
109
|
+
ret = ""
|
110
|
+
len.times{ ret << RAND_CHARS[rand(rand_max)] }
|
111
|
+
ret
|
112
|
+
end
|
113
|
+
module_function :random_string
|
114
|
+
|
115
|
+
###########
|
116
|
+
|
117
|
+
require "thread"
|
118
|
+
require "timeout"
|
119
|
+
require "singleton"
|
120
|
+
|
121
|
+
##
|
122
|
+
# Class used to manage timeout handlers across multiple threads.
|
123
|
+
#
|
124
|
+
# Timeout handlers should be managed by using the class methods which are
|
125
|
+
# synchronized.
|
126
|
+
#
|
127
|
+
# id = TimeoutHandler.register(10, Timeout::Error)
|
128
|
+
# begin
|
129
|
+
# sleep 20
|
130
|
+
# puts 'foo'
|
131
|
+
# ensure
|
132
|
+
# TimeoutHandler.cancel(id)
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# will raise Timeout::Error
|
136
|
+
#
|
137
|
+
# id = TimeoutHandler.register(10, Timeout::Error)
|
138
|
+
# begin
|
139
|
+
# sleep 5
|
140
|
+
# puts 'foo'
|
141
|
+
# ensure
|
142
|
+
# TimeoutHandler.cancel(id)
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# will print 'foo'
|
146
|
+
#
|
147
|
+
class TimeoutHandler
|
148
|
+
include Singleton
|
149
|
+
|
150
|
+
##
|
151
|
+
# Mutex used to synchronize access across threads
|
152
|
+
TimeoutMutex = Mutex.new # :nodoc:
|
153
|
+
|
154
|
+
##
|
155
|
+
# Registers a new timeout handler
|
156
|
+
#
|
157
|
+
# +time+:: Timeout in seconds
|
158
|
+
# +exception+:: Exception to raise when timeout elapsed
|
159
|
+
def TimeoutHandler.register(seconds, exception)
|
160
|
+
TimeoutMutex.synchronize{
|
161
|
+
instance.register(Thread.current, Time.now + seconds, exception)
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Cancels the timeout handler +id+
|
167
|
+
def TimeoutHandler.cancel(id)
|
168
|
+
TimeoutMutex.synchronize{
|
169
|
+
instance.cancel(Thread.current, id)
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def initialize
|
174
|
+
@timeout_info = Hash.new
|
175
|
+
Thread.start{
|
176
|
+
while true
|
177
|
+
now = Time.now
|
178
|
+
@timeout_info.each{|thread, ary|
|
179
|
+
ary.dup.each{|info|
|
180
|
+
time, exception = *info
|
181
|
+
interrupt(thread, info.object_id, exception) if time < now
|
182
|
+
}
|
183
|
+
}
|
184
|
+
sleep 0.5
|
185
|
+
end
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Interrupts the timeout handler +id+ and raises +exception+
|
191
|
+
def interrupt(thread, id, exception)
|
192
|
+
TimeoutMutex.synchronize{
|
193
|
+
if cancel(thread, id) && thread.alive?
|
194
|
+
thread.raise(exception, "execution timeout")
|
195
|
+
end
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
##
|
200
|
+
# Registers a new timeout handler
|
201
|
+
#
|
202
|
+
# +time+:: Timeout in seconds
|
203
|
+
# +exception+:: Exception to raise when timeout elapsed
|
204
|
+
def register(thread, time, exception)
|
205
|
+
@timeout_info[thread] ||= Array.new
|
206
|
+
@timeout_info[thread] << [time, exception]
|
207
|
+
return @timeout_info[thread].last.object_id
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Cancels the timeout handler +id+
|
212
|
+
def cancel(thread, id)
|
213
|
+
if ary = @timeout_info[thread]
|
214
|
+
ary.delete_if{|info| info.object_id == id }
|
215
|
+
if ary.empty?
|
216
|
+
@timeout_info.delete(thread)
|
217
|
+
end
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
return false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Executes the passed block and raises +exception+ if execution takes more
|
226
|
+
# than +seconds+.
|
227
|
+
#
|
228
|
+
# If +seconds+ is zero or nil, simply executes the block
|
229
|
+
def timeout(seconds, exception=Timeout::Error)
|
230
|
+
return yield if seconds.nil? or seconds.zero?
|
231
|
+
# raise ThreadError, "timeout within critical session" if Thread.critical
|
232
|
+
id = TimeoutHandler.register(seconds, exception)
|
233
|
+
begin
|
234
|
+
yield(seconds)
|
235
|
+
ensure
|
236
|
+
TimeoutHandler.cancel(id)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
module_function :timeout
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#--
|
2
|
+
# version.rb -- version and release date
|
3
|
+
#
|
4
|
+
# Author: IPR -- Internet Programming with Ruby -- writers
|
5
|
+
# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
|
6
|
+
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
|
7
|
+
# reserved.
|
8
|
+
#
|
9
|
+
# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $
|
10
|
+
|
11
|
+
module WEBrick
|
12
|
+
VERSION = "1.3.1"
|
13
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "pp"
|
2
|
+
|
3
|
+
module DemoApplication
|
4
|
+
def initialize(config, enctype)
|
5
|
+
super
|
6
|
+
@enctype = enctype
|
7
|
+
end
|
8
|
+
|
9
|
+
def do_GET(req, res)
|
10
|
+
if req.path_info != "/"
|
11
|
+
res.set_redirect(WEBrick::HTTPStatus::Found, req.script_name + "/")
|
12
|
+
end
|
13
|
+
res.body =<<-_end_of_html_
|
14
|
+
<HTML>
|
15
|
+
<FORM method="POST" enctype=#{@enctype}>
|
16
|
+
text: <INPUT type="text" name="text"><BR>
|
17
|
+
file: <INPUT type="file" name="file"><BR>
|
18
|
+
check:
|
19
|
+
<INPUT type="checkbox" name="check" value="a">a,
|
20
|
+
<INPUT type="checkbox" name="check" value="b">b,
|
21
|
+
<INPUT type="checkbox" name="check" value="c">c,
|
22
|
+
<BR>
|
23
|
+
<INPUT type="submit">
|
24
|
+
</FORM>
|
25
|
+
</HTML>
|
26
|
+
_end_of_html_
|
27
|
+
res['content-type'] = 'text/html; charset=iso-8859-1'
|
28
|
+
end
|
29
|
+
|
30
|
+
def do_POST(req, res)
|
31
|
+
if req["content-length"].to_i > 1024*10
|
32
|
+
raise WEBrick::HTTPStatus::Forbidden, "file size too large"
|
33
|
+
end
|
34
|
+
res.body =<<-_end_of_html_
|
35
|
+
<HTML>
|
36
|
+
<H2>Query Parameters</H2>
|
37
|
+
#{display_query(req.query)}
|
38
|
+
<A href="#{req.path}">return</A>
|
39
|
+
<H2>Request</H2>
|
40
|
+
<PRE>#{WEBrick::HTMLUtils::escape(PP::pp(req, "", 80))}</PRE>
|
41
|
+
<H2>Response</H2>
|
42
|
+
<PRE>#{WEBrick::HTMLUtils::escape(PP::pp(res, "", 80))}</PRE>
|
43
|
+
</HTML>
|
44
|
+
_end_of_html_
|
45
|
+
res['content-type'] = 'text/html; charset=iso-8859-1'
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def display_query(q)
|
51
|
+
ret = ""
|
52
|
+
q.each{|key, val|
|
53
|
+
ret << "<H3>#{WEBrick::HTMLUtils::escape(key)}</H3>"
|
54
|
+
ret << "<TABLE border=1>"
|
55
|
+
ret << make_tr("val", val.inspect)
|
56
|
+
ret << make_tr("val.to_a", val.to_a.inspect)
|
57
|
+
ret << make_tr("val.to_ary", val.to_ary.inspect)
|
58
|
+
ret << "</TABLE>"
|
59
|
+
}
|
60
|
+
ret
|
61
|
+
end
|
62
|
+
|
63
|
+
def make_tr(arg0, arg1)
|
64
|
+
"<TR><TD>#{arg0}</TD><TD>#{WEBrick::HTMLUtils::escape(arg1)}</TD></TR>"
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "webrick/cgi"
|
3
|
+
require "webrick/https" # should load if it runs on HTTPS server
|
4
|
+
require "./demo-app"
|
5
|
+
|
6
|
+
class DemoCGI < WEBrick::CGI
|
7
|
+
include DemoApplication
|
8
|
+
end
|
9
|
+
|
10
|
+
config = { :NPH => false }
|
11
|
+
cgi = DemoCGI.new(config, "multipart/form-data")
|
12
|
+
cgi.start
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "webrick/cgi"
|
3
|
+
require "webrick/https" # should load if it runs on HTTPS server
|
4
|
+
require "./demo-app"
|
5
|
+
|
6
|
+
class DemoCGI < WEBrick::CGI
|
7
|
+
include DemoApplication
|
8
|
+
end
|
9
|
+
|
10
|
+
config = { :NPH => false }
|
11
|
+
cgi = DemoCGI.new(config, "application/x-www-form-urlencoded")
|
12
|
+
cgi.start
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "webrick"
|
2
|
+
|
3
|
+
httpd = WEBrick::HTTPServer.new(
|
4
|
+
:DocumentRoot => File::dirname(__FILE__),
|
5
|
+
:Port => 10080,
|
6
|
+
:Logger => WEBrick::Log.new($stderr, WEBrick::Log::DEBUG),
|
7
|
+
:AccessLog => [
|
8
|
+
[ $stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
|
9
|
+
[ $stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT ],
|
10
|
+
[ $stderr, WEBrick::AccessLog::AGENT_LOG_FORMAT ],
|
11
|
+
],
|
12
|
+
:CGIPathEnv => ENV["PATH"] # PATH environment variable for CGI.
|
13
|
+
)
|
14
|
+
|
15
|
+
require "./hello"
|
16
|
+
httpd.mount("/hello", HelloServlet)
|
17
|
+
|
18
|
+
require "./demo-servlet"
|
19
|
+
httpd.mount("/urlencoded", DemoServlet, "application/x-www-form-urlencoded")
|
20
|
+
httpd.mount("/multipart", DemoServlet, "multipart/form-data")
|
21
|
+
|
22
|
+
trap(:INT){ httpd.shutdown }
|
23
|
+
httpd.start
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "webrick"
|
2
|
+
require "webrick/httpproxy"
|
3
|
+
|
4
|
+
# The :ProxyContentHandler proc will be invoked before sending a response to
|
5
|
+
# the User-Agent. You can inspect the pair of request and response messages
|
6
|
+
# (or edit the response message if necessary).
|
7
|
+
|
8
|
+
pch = Proc.new{|req, res|
|
9
|
+
p [ req.request_line, res.status_line ]
|
10
|
+
}
|
11
|
+
|
12
|
+
def upstream_proxy
|
13
|
+
if prx = ENV["http_proxy"]
|
14
|
+
return URI.parse(prx)
|
15
|
+
end
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
|
19
|
+
httpd = WEBrick::HTTPProxyServer.new(
|
20
|
+
:Port => 10080,
|
21
|
+
:ProxyContentHandler => pch,
|
22
|
+
:ProxyURI => upstream_proxy
|
23
|
+
)
|
24
|
+
Signal.trap(:INT){ httpd.shutdown }
|
25
|
+
httpd.start
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "webrick"
|
2
|
+
require "webrick/https"
|
3
|
+
|
4
|
+
hostname = WEBrick::Utils::getservername
|
5
|
+
subject = [["O", "ruby-lang.org"], ["OU", "sample"], ["CN", hostname]]
|
6
|
+
comment = "Comment for self-signed certificate"
|
7
|
+
|
8
|
+
httpd = WEBrick::HTTPServer.new(
|
9
|
+
:DocumentRoot => File::dirname(__FILE__),
|
10
|
+
:Port => 10443,
|
11
|
+
:SSLEnable => true,
|
12
|
+
|
13
|
+
# Specify key pair and server certificate.
|
14
|
+
# :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read("server.key")),
|
15
|
+
# :SSLCertificate => OpenSSL::X509::Certificate.new(File.read("server.crt")),
|
16
|
+
|
17
|
+
# specify the following SSL options if you want to use auto
|
18
|
+
# generated self-signed certificate.
|
19
|
+
:SSLCertName => subject,
|
20
|
+
:SSLComment => comment,
|
21
|
+
|
22
|
+
:CGIPathEnv => ENV["PATH"] # PATH environment variable for CGI.
|
23
|
+
)
|
24
|
+
|
25
|
+
require "./hello"
|
26
|
+
httpd.mount("/hello", HelloServlet)
|
27
|
+
|
28
|
+
require "./demo-servlet"
|
29
|
+
httpd.mount("/urlencoded", DemoServlet, "application/x-www-form-urlencoded")
|
30
|
+
httpd.mount("/multipart", DemoServlet, "multipart/form-data")
|
31
|
+
|
32
|
+
trap(:INT){ httpd.shutdown }
|
33
|
+
httpd.start
|