webrick 1.3.1 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +63 -0
  5. data/Rakefile +10 -0
  6. data/bin/console +14 -0
  7. data/bin/setup +8 -0
  8. data/lib/webrick.rb +7 -7
  9. data/lib/webrick/accesslog.rb +12 -6
  10. data/lib/webrick/cgi.rb +58 -5
  11. data/lib/webrick/compat.rb +2 -1
  12. data/lib/webrick/config.rb +47 -10
  13. data/lib/webrick/cookie.rb +69 -7
  14. data/lib/webrick/htmlutils.rb +4 -2
  15. data/lib/webrick/httpauth.rb +6 -5
  16. data/lib/webrick/httpauth/authenticator.rb +13 -8
  17. data/lib/webrick/httpauth/basicauth.rb +16 -8
  18. data/lib/webrick/httpauth/digestauth.rb +35 -32
  19. data/lib/webrick/httpauth/htdigest.rb +12 -8
  20. data/lib/webrick/httpauth/htgroup.rb +10 -6
  21. data/lib/webrick/httpauth/htpasswd.rb +46 -9
  22. data/lib/webrick/httpauth/userdb.rb +1 -0
  23. data/lib/webrick/httpproxy.rb +93 -48
  24. data/lib/webrick/httprequest.rb +201 -31
  25. data/lib/webrick/httpresponse.rb +235 -70
  26. data/lib/webrick/https.rb +90 -2
  27. data/lib/webrick/httpserver.rb +45 -15
  28. data/lib/webrick/httpservlet.rb +6 -5
  29. data/lib/webrick/httpservlet/abstract.rb +5 -6
  30. data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
  31. data/lib/webrick/httpservlet/cgihandler.rb +29 -11
  32. data/lib/webrick/httpservlet/erbhandler.rb +4 -3
  33. data/lib/webrick/httpservlet/filehandler.rb +136 -65
  34. data/lib/webrick/httpservlet/prochandler.rb +15 -1
  35. data/lib/webrick/httpstatus.rb +24 -14
  36. data/lib/webrick/httputils.rb +134 -17
  37. data/lib/webrick/httpversion.rb +28 -1
  38. data/lib/webrick/log.rb +25 -5
  39. data/lib/webrick/server.rb +234 -74
  40. data/lib/webrick/ssl.rb +100 -12
  41. data/lib/webrick/utils.rb +98 -69
  42. data/lib/webrick/version.rb +6 -1
  43. data/webrick.gemspec +76 -0
  44. metadata +73 -72
  45. data/README.txt +0 -21
  46. data/sample/webrick/demo-app.rb +0 -66
  47. data/sample/webrick/demo-multipart.cgi +0 -12
  48. data/sample/webrick/demo-servlet.rb +0 -6
  49. data/sample/webrick/demo-urlencoded.cgi +0 -12
  50. data/sample/webrick/hello.cgi +0 -11
  51. data/sample/webrick/hello.rb +0 -8
  52. data/sample/webrick/httpd.rb +0 -23
  53. data/sample/webrick/httpproxy.rb +0 -25
  54. data/sample/webrick/httpsd.rb +0 -33
  55. data/test/openssl/utils.rb +0 -313
  56. data/test/ruby/envutil.rb +0 -208
  57. data/test/webrick/test_cgi.rb +0 -134
  58. data/test/webrick/test_cookie.rb +0 -131
  59. data/test/webrick/test_filehandler.rb +0 -285
  60. data/test/webrick/test_httpauth.rb +0 -167
  61. data/test/webrick/test_httpproxy.rb +0 -282
  62. data/test/webrick/test_httprequest.rb +0 -411
  63. data/test/webrick/test_httpresponse.rb +0 -49
  64. data/test/webrick/test_httpserver.rb +0 -305
  65. data/test/webrick/test_httputils.rb +0 -96
  66. data/test/webrick/test_httpversion.rb +0 -40
  67. data/test/webrick/test_server.rb +0 -67
  68. data/test/webrick/test_utils.rb +0 -64
  69. data/test/webrick/utils.rb +0 -58
  70. data/test/webrick/webrick.cgi +0 -36
  71. data/test/webrick/webrick_long_filename.cgi +0 -36
data/lib/webrick/ssl.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # ssl.rb -- SSL/TLS enhancement for GenericServer
3
4
  #
@@ -12,6 +13,57 @@ module WEBrick
12
13
  module Config
13
14
  svrsoft = General[:ServerSoftware]
14
15
  osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
16
+
17
+ ##
18
+ # Default SSL server configuration.
19
+ #
20
+ # WEBrick can automatically create a self-signed certificate if
21
+ # <code>:SSLCertName</code> is set. For more information on the various
22
+ # SSL options see OpenSSL::SSL::SSLContext.
23
+ #
24
+ # :ServerSoftware ::
25
+ # The server software name used in the Server: header.
26
+ # :SSLEnable :: false,
27
+ # Enable SSL for this server. Defaults to false.
28
+ # :SSLCertificate ::
29
+ # The SSL certificate for the server.
30
+ # :SSLPrivateKey ::
31
+ # The SSL private key for the server certificate.
32
+ # :SSLClientCA :: nil,
33
+ # Array of certificates that will be sent to the client.
34
+ # :SSLExtraChainCert :: nil,
35
+ # Array of certificates that will be added to the certificate chain
36
+ # :SSLCACertificateFile :: nil,
37
+ # Path to a CA certificate file
38
+ # :SSLCACertificatePath :: nil,
39
+ # Path to a directory containing CA certificates
40
+ # :SSLCertificateStore :: nil,
41
+ # OpenSSL::X509::Store used for certificate validation of the client
42
+ # :SSLTmpDhCallback :: nil,
43
+ # Callback invoked when DH parameters are required.
44
+ # :SSLVerifyClient ::
45
+ # Sets whether the client is verified. This defaults to VERIFY_NONE
46
+ # which is typical for an HTTPS server.
47
+ # :SSLVerifyDepth ::
48
+ # Number of CA certificates to walk when verifying a certificate chain
49
+ # :SSLVerifyCallback ::
50
+ # Custom certificate verification callback
51
+ # :SSLServerNameCallback::
52
+ # Custom servername indication callback
53
+ # :SSLTimeout ::
54
+ # Maximum session lifetime
55
+ # :SSLOptions ::
56
+ # Various SSL options
57
+ # :SSLCiphers ::
58
+ # Ciphers to be used
59
+ # :SSLStartImmediately ::
60
+ # Immediately start SSL upon connection? Defaults to true
61
+ # :SSLCertName ::
62
+ # SSL certificate name. Must be set to enable automatic certificate
63
+ # creation.
64
+ # :SSLCertComment ::
65
+ # Comment used during automatic certificate creation.
66
+
15
67
  SSL = {
16
68
  :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}",
17
69
  :SSLEnable => false,
@@ -22,11 +74,13 @@ module WEBrick
22
74
  :SSLCACertificateFile => nil,
23
75
  :SSLCACertificatePath => nil,
24
76
  :SSLCertificateStore => nil,
77
+ :SSLTmpDhCallback => nil,
25
78
  :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
26
79
  :SSLVerifyDepth => nil,
27
80
  :SSLVerifyCallback => nil, # custom verification
28
81
  :SSLTimeout => nil,
29
82
  :SSLOptions => nil,
83
+ :SSLCiphers => nil,
30
84
  :SSLStartImmediately => true,
31
85
  # Must specify if you use auto generated certificate.
32
86
  :SSLCertName => nil,
@@ -36,6 +90,10 @@ module WEBrick
36
90
  end
37
91
 
38
92
  module Utils
93
+ ##
94
+ # Creates a self-signed certificate with the given number of +bits+,
95
+ # the issuer +cn+ and a +comment+ to be stored in the certificate.
96
+
39
97
  def create_self_signed_cert(bits, cn, comment)
40
98
  rsa = OpenSSL::PKey::RSA.new(bits){|p, n|
41
99
  case p
@@ -52,7 +110,8 @@ module WEBrick
52
110
  cert = OpenSSL::X509::Certificate.new
53
111
  cert.version = 2
54
112
  cert.serial = 1
55
- name = OpenSSL::X509::Name.new(cn)
113
+ name = (cn.kind_of? String) ? OpenSSL::X509::Name.parse(cn)
114
+ : OpenSSL::X509::Name.new(cn)
56
115
  cert.subject = name
57
116
  cert.issuer = name
58
117
  cert.not_before = Time.now
@@ -71,26 +130,40 @@ module WEBrick
71
130
  aki = ef.create_extension("authorityKeyIdentifier",
72
131
  "keyid:always,issuer:always")
73
132
  cert.add_extension(aki)
74
- cert.sign(rsa, OpenSSL::Digest::SHA1.new)
133
+ cert.sign(rsa, OpenSSL::Digest::SHA256.new)
75
134
 
76
135
  return [ cert, rsa ]
77
136
  end
78
137
  module_function :create_self_signed_cert
79
138
  end
80
139
 
140
+ ##
141
+ #--
142
+ # Updates WEBrick::GenericServer with SSL functionality
143
+
81
144
  class GenericServer
82
- def ssl_context
83
- @ssl_context ||= nil
145
+
146
+ ##
147
+ # SSL context for the server when run in SSL mode
148
+
149
+ def ssl_context # :nodoc:
150
+ @ssl_context ||= begin
151
+ if @config[:SSLEnable]
152
+ ssl_context = setup_ssl_context(@config)
153
+ @logger.info("\n" + @config[:SSLCertificate].to_text)
154
+ ssl_context
155
+ end
156
+ end
84
157
  end
85
158
 
86
159
  undef listen
87
- def listen(address, port)
88
- listeners = Utils::create_listeners(address, port, @logger)
160
+
161
+ ##
162
+ # Updates +listen+ to enable SSL when the SSL configuration is active.
163
+
164
+ def listen(address, port) # :nodoc:
165
+ listeners = Utils::create_listeners(address, port)
89
166
  if @config[:SSLEnable]
90
- unless ssl_context
91
- @ssl_context = setup_ssl_context(@config)
92
- @logger.info("\n" + @config[:SSLCertificate].to_text)
93
- end
94
167
  listeners.collect!{|svr|
95
168
  ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
96
169
  ssvr.start_immediately = @config[:SSLStartImmediately]
@@ -98,13 +171,17 @@ module WEBrick
98
171
  }
99
172
  end
100
173
  @listeners += listeners
174
+ setup_shutdown_pipe
101
175
  end
102
176
 
103
- def setup_ssl_context(config)
177
+ ##
178
+ # Sets up an SSL context for +config+
179
+
180
+ def setup_ssl_context(config) # :nodoc:
104
181
  unless config[:SSLCertificate]
105
182
  cn = config[:SSLCertName]
106
183
  comment = config[:SSLCertComment]
107
- cert, key = Utils::create_self_signed_cert(1024, cn, comment)
184
+ cert, key = Utils::create_self_signed_cert(2048, cn, comment)
108
185
  config[:SSLCertificate] = cert
109
186
  config[:SSLPrivateKey] = key
110
187
  end
@@ -116,12 +193,23 @@ module WEBrick
116
193
  ctx.ca_file = config[:SSLCACertificateFile]
117
194
  ctx.ca_path = config[:SSLCACertificatePath]
118
195
  ctx.cert_store = config[:SSLCertificateStore]
196
+ ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
119
197
  ctx.verify_mode = config[:SSLVerifyClient]
120
198
  ctx.verify_depth = config[:SSLVerifyDepth]
121
199
  ctx.verify_callback = config[:SSLVerifyCallback]
200
+ ctx.servername_cb = config[:SSLServerNameCallback] || proc { |args| ssl_servername_callback(*args) }
122
201
  ctx.timeout = config[:SSLTimeout]
123
202
  ctx.options = config[:SSLOptions]
203
+ ctx.ciphers = config[:SSLCiphers]
124
204
  ctx
125
205
  end
206
+
207
+ ##
208
+ # ServerNameIndication callback
209
+
210
+ def ssl_servername_callback(sslsocket, hostname = nil)
211
+ # default
212
+ end
213
+
126
214
  end
127
215
  end
data/lib/webrick/utils.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # utils.rb -- Miscellaneous utilities
3
4
  #
@@ -9,45 +10,34 @@
9
10
  # $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
10
11
 
11
12
  require 'socket'
12
- require 'fcntl'
13
- begin
14
- require 'etc'
15
- rescue LoadError
16
- nil
17
- end
13
+ require 'io/nonblock'
14
+ require 'etc'
18
15
 
19
16
  module WEBrick
20
17
  module Utils
21
18
  ##
22
19
  # Sets IO operations on +io+ to be non-blocking
23
20
  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)
21
+ io.nonblock = true if io.respond_to?(:nonblock=)
29
22
  end
30
23
  module_function :set_non_blocking
31
24
 
32
25
  ##
33
26
  # Sets the close on exec flag for +io+
34
27
  def set_close_on_exec(io)
35
- if defined?(Fcntl::FD_CLOEXEC)
36
- io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
37
- end
28
+ io.close_on_exec = true if io.respond_to?(:close_on_exec=)
38
29
  end
39
30
  module_function :set_close_on_exec
40
31
 
41
32
  ##
42
33
  # Changes the process's uid and gid to the ones of +user+
43
34
  def su(user)
44
- if defined?(Etc)
45
- pw = Etc.getpwnam(user)
35
+ if pw = Etc.getpwnam(user)
46
36
  Process::initgroups(user, pw.gid)
47
37
  Process::Sys::setgid(pw.gid)
48
38
  Process::Sys::setuid(pw.uid)
49
39
  else
50
- warn("WEBrick::Utils::su doesn't work on this platform")
40
+ warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1)
51
41
  end
52
42
  end
53
43
  module_function :su
@@ -68,30 +58,17 @@ module WEBrick
68
58
  # Creates TCP server sockets bound to +address+:+port+ and returns them.
69
59
  #
70
60
  # It will create IPV4 and IPV6 sockets on all interfaces.
71
- def create_listeners(address, port, logger=nil)
61
+ def create_listeners(address, port)
72
62
  unless port
73
63
  raise ArgumentError, "must specify port"
74
64
  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
65
+ sockets = Socket.tcp_server_sockets(address, port)
66
+ sockets = sockets.map {|s|
67
+ s.autoclose = false
68
+ ts = TCPServer.for_fd(s.fileno)
69
+ s.close
70
+ ts
93
71
  }
94
- raise last_error if sockets.empty?
95
72
  return sockets
96
73
  end
97
74
  module_function :create_listeners
@@ -114,7 +91,6 @@ module WEBrick
114
91
 
115
92
  ###########
116
93
 
117
- require "thread"
118
94
  require "timeout"
119
95
  require "singleton"
120
96
 
@@ -149,7 +125,7 @@ module WEBrick
149
125
 
150
126
  ##
151
127
  # Mutex used to synchronize access across threads
152
- TimeoutMutex = Mutex.new # :nodoc:
128
+ TimeoutMutex = Thread::Mutex.new # :nodoc:
153
129
 
154
130
  ##
155
131
  # Registers a new timeout handler
@@ -157,43 +133,82 @@ module WEBrick
157
133
  # +time+:: Timeout in seconds
158
134
  # +exception+:: Exception to raise when timeout elapsed
159
135
  def TimeoutHandler.register(seconds, exception)
160
- TimeoutMutex.synchronize{
161
- instance.register(Thread.current, Time.now + seconds, exception)
162
- }
136
+ at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
137
+ instance.register(Thread.current, at, exception)
163
138
  end
164
139
 
165
140
  ##
166
141
  # Cancels the timeout handler +id+
167
142
  def TimeoutHandler.cancel(id)
143
+ instance.cancel(Thread.current, id)
144
+ end
145
+
146
+ def self.terminate
147
+ instance.terminate
148
+ end
149
+
150
+ ##
151
+ # Creates a new TimeoutHandler. You should use ::register and ::cancel
152
+ # instead of creating the timeout handler directly.
153
+ def initialize
168
154
  TimeoutMutex.synchronize{
169
- instance.cancel(Thread.current, id)
155
+ @timeout_info = Hash.new
170
156
  }
157
+ @queue = Thread::Queue.new
158
+ @watcher = nil
171
159
  end
172
160
 
173
- def initialize
174
- @timeout_info = Hash.new
175
- Thread.start{
161
+ # :nodoc:
162
+ private \
163
+ def watch
164
+ to_interrupt = []
176
165
  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
166
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
167
+ wakeup = nil
168
+ to_interrupt.clear
169
+ TimeoutMutex.synchronize{
170
+ @timeout_info.each {|thread, ary|
171
+ next unless ary
172
+ ary.each{|info|
173
+ time, exception = *info
174
+ if time < now
175
+ to_interrupt.push [thread, info.object_id, exception]
176
+ elsif !wakeup || time < wakeup
177
+ wakeup = time
178
+ end
179
+ }
182
180
  }
183
181
  }
184
- sleep 0.5
182
+ to_interrupt.each {|arg| interrupt(*arg)}
183
+ if !wakeup
184
+ @queue.pop
185
+ elsif (wakeup -= now) > 0
186
+ begin
187
+ (th = Thread.start {@queue.pop}).join(wakeup)
188
+ ensure
189
+ th&.kill&.join
190
+ end
191
+ end
192
+ @queue.clear
185
193
  end
186
- }
187
- end
194
+ end
195
+
196
+ # :nodoc:
197
+ private \
198
+ def watcher
199
+ (w = @watcher)&.alive? and return w # usual case
200
+ TimeoutMutex.synchronize{
201
+ (w = @watcher)&.alive? and next w # pathological check
202
+ @watcher = Thread.start(&method(:watch))
203
+ }
204
+ end
188
205
 
189
206
  ##
190
207
  # Interrupts the timeout handler +id+ and raises +exception+
191
208
  def interrupt(thread, id, exception)
192
- TimeoutMutex.synchronize{
193
- if cancel(thread, id) && thread.alive?
194
- thread.raise(exception, "execution timeout")
195
- end
196
- }
209
+ if cancel(thread, id) && thread.alive?
210
+ thread.raise(exception, "execution timeout")
211
+ end
197
212
  end
198
213
 
199
214
  ##
@@ -202,22 +217,36 @@ module WEBrick
202
217
  # +time+:: Timeout in seconds
203
218
  # +exception+:: Exception to raise when timeout elapsed
204
219
  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
220
+ info = nil
221
+ TimeoutMutex.synchronize{
222
+ (@timeout_info[thread] ||= []) << (info = [time, exception])
223
+ }
224
+ @queue.push nil
225
+ watcher
226
+ return info.object_id
208
227
  end
209
228
 
210
229
  ##
211
230
  # Cancels the timeout handler +id+
212
231
  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)
232
+ TimeoutMutex.synchronize{
233
+ if ary = @timeout_info[thread]
234
+ ary.delete_if{|info| info.object_id == id }
235
+ if ary.empty?
236
+ @timeout_info.delete(thread)
237
+ end
238
+ return true
217
239
  end
218
- return true
219
- end
220
- return false
240
+ return false
241
+ }
242
+ end
243
+
244
+ ##
245
+ def terminate
246
+ TimeoutMutex.synchronize{
247
+ @timeout_info.clear
248
+ @watcher&.kill&.join
249
+ }
221
250
  end
222
251
  end
223
252
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # version.rb -- version and release date
3
4
  #
@@ -9,5 +10,9 @@
9
10
  # $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $
10
11
 
11
12
  module WEBrick
12
- VERSION = "1.3.1"
13
+
14
+ ##
15
+ # The WEBrick version
16
+
17
+ VERSION = "1.6.1"
13
18
  end