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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # cookie.rb -- Cookie class
3
4
  #
@@ -9,17 +10,59 @@
9
10
  # $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
10
11
 
11
12
  require 'time'
12
- require 'webrick/httputils'
13
+ require_relative 'httputils'
13
14
 
14
15
  module WEBrick
16
+
17
+ ##
18
+ # Processes HTTP cookies
19
+
15
20
  class Cookie
16
21
 
22
+ ##
23
+ # The cookie name
24
+
17
25
  attr_reader :name
18
- attr_accessor :value, :version
19
- attr_accessor :domain, :path, :secure
20
- attr_accessor :comment, :max_age
26
+
27
+ ##
28
+ # The cookie value
29
+
30
+ attr_accessor :value
31
+
32
+ ##
33
+ # The cookie version
34
+
35
+ attr_accessor :version
36
+
37
+ ##
38
+ # The cookie domain
39
+ attr_accessor :domain
40
+
41
+ ##
42
+ # The cookie path
43
+
44
+ attr_accessor :path
45
+
46
+ ##
47
+ # Is this a secure cookie?
48
+
49
+ attr_accessor :secure
50
+
51
+ ##
52
+ # The cookie comment
53
+
54
+ attr_accessor :comment
55
+
56
+ ##
57
+ # The maximum age of the cookie
58
+
59
+ attr_accessor :max_age
60
+
21
61
  #attr_accessor :comment_url, :discard, :port
22
62
 
63
+ ##
64
+ # Creates a new cookie with the given +name+ and +value+
65
+
23
66
  def initialize(name, value)
24
67
  @name = name
25
68
  @value = value
@@ -29,14 +72,25 @@ module WEBrick
29
72
  @expires = @comment_url = @discard = @port = nil
30
73
  end
31
74
 
75
+ ##
76
+ # Sets the cookie expiration to the time +t+. The expiration time may be
77
+ # a false value to disable expiration or a Time or HTTP format time string
78
+ # to set the expiration date.
79
+
32
80
  def expires=(t)
33
81
  @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
34
82
  end
35
83
 
84
+ ##
85
+ # Retrieves the expiration time as a Time
86
+
36
87
  def expires
37
88
  @expires && Time.parse(@expires)
38
89
  end
39
90
 
91
+ ##
92
+ # The cookie string suitable for use in an HTTP header
93
+
40
94
  def to_s
41
95
  ret = ""
42
96
  ret << @name << "=" << @value
@@ -50,14 +104,16 @@ module WEBrick
50
104
  ret
51
105
  end
52
106
 
53
- # Cookie::parse()
54
- # It parses Cookie field sent from the user agent.
107
+ ##
108
+ # Parses a Cookie field sent from the user-agent. Returns an array of
109
+ # cookies.
110
+
55
111
  def self.parse(str)
56
112
  if str
57
113
  ret = []
58
114
  cookie = nil
59
115
  ver = 0
60
- str.split(/[;,]\s+/).each{|x|
116
+ str.split(/;\s+/).each{|x|
61
117
  key, val = x.split(/=/,2)
62
118
  val = val ? HTTPUtils::dequote(val) : ""
63
119
  case key
@@ -76,6 +132,9 @@ module WEBrick
76
132
  end
77
133
  end
78
134
 
135
+ ##
136
+ # Parses the cookie in +str+
137
+
79
138
  def self.parse_set_cookie(str)
80
139
  cookie_elem = str.split(/;/)
81
140
  first_elem = cookie_elem.shift
@@ -101,6 +160,9 @@ module WEBrick
101
160
  return cookie
102
161
  end
103
162
 
163
+ ##
164
+ # Parses the cookies in +str+
165
+
104
166
  def self.parse_set_cookies(str)
105
167
  return str.split(/,(?=[^;,]*=)|,$/).collect{|c|
106
168
  parse_set_cookie(c)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # htmlutils.rb -- HTMLUtils Module
3
4
  #
@@ -15,12 +16,13 @@ module WEBrick
15
16
  # Escapes &, ", > and < in +string+
16
17
 
17
18
  def escape(string)
18
- str = string ? string.dup : ""
19
+ return "" unless string
20
+ str = string.b
19
21
  str.gsub!(/&/n, '&amp;')
20
22
  str.gsub!(/\"/n, '&quot;')
21
23
  str.gsub!(/>/n, '&gt;')
22
24
  str.gsub!(/</n, '&lt;')
23
- str
25
+ str.force_encoding(string.encoding)
24
26
  end
25
27
  module_function :escape
26
28
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth.rb -- HTTP access authentication
3
4
  #
@@ -8,11 +9,11 @@
8
9
  #
9
10
  # $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
10
11
 
11
- require 'webrick/httpauth/basicauth'
12
- require 'webrick/httpauth/digestauth'
13
- require 'webrick/httpauth/htpasswd'
14
- require 'webrick/httpauth/htdigest'
15
- require 'webrick/httpauth/htgroup'
12
+ require_relative 'httpauth/basicauth'
13
+ require_relative 'httpauth/digestauth'
14
+ require_relative 'httpauth/htpasswd'
15
+ require_relative 'httpauth/htdigest'
16
+ require_relative 'httpauth/htgroup'
16
17
 
17
18
  module WEBrick
18
19
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # httpauth/authenticator.rb -- Authenticator mix-in module.
3
4
  #
@@ -16,10 +17,10 @@ module WEBrick
16
17
 
17
18
  module Authenticator
18
19
 
19
- RequestField = "Authorization"
20
- ResponseField = "WWW-Authenticate"
21
- ResponseInfoField = "Authentication-Info"
22
- AuthException = HTTPStatus::Unauthorized
20
+ RequestField = "Authorization" # :nodoc:
21
+ ResponseField = "WWW-Authenticate" # :nodoc:
22
+ ResponseInfoField = "Authentication-Info" # :nodoc:
23
+ AuthException = HTTPStatus::Unauthorized # :nodoc:
23
24
 
24
25
  ##
25
26
  # Method of authentication, must be overridden by the including class
@@ -43,6 +44,8 @@ module WEBrick
43
44
 
44
45
  private
45
46
 
47
+ # :stopdoc:
48
+
46
49
  ##
47
50
  # Initializes the authenticator from +config+
48
51
 
@@ -96,6 +99,8 @@ module WEBrick
96
99
  log(:info, fmt, *args)
97
100
  end
98
101
  end
102
+
103
+ # :startdoc:
99
104
  end
100
105
 
101
106
  ##
@@ -103,10 +108,10 @@ module WEBrick
103
108
  # authentication schemes for proxies.
104
109
 
105
110
  module ProxyAuthenticator
106
- RequestField = "Proxy-Authorization"
107
- ResponseField = "Proxy-Authenticate"
108
- InfoField = "Proxy-Authentication-Info"
109
- AuthException = HTTPStatus::ProxyAuthenticationRequired
111
+ RequestField = "Proxy-Authorization" # :nodoc:
112
+ ResponseField = "Proxy-Authenticate" # :nodoc:
113
+ InfoField = "Proxy-Authentication-Info" # :nodoc:
114
+ AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc:
110
115
  end
111
116
  end
112
117
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/basicauth.rb -- HTTP basic access authentication
3
4
  #
@@ -7,9 +8,9 @@
7
8
  #
8
9
  # $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
9
10
 
10
- require 'webrick/config'
11
- require 'webrick/httpstatus'
12
- require 'webrick/httpauth/authenticator'
11
+ require_relative '../config'
12
+ require_relative '../httpstatus'
13
+ require_relative 'authenticator'
13
14
 
14
15
  module WEBrick
15
16
  module HTTPAuth
@@ -23,7 +24,7 @@ module WEBrick
23
24
  #
24
25
  # config = { :Realm => 'BasicAuth example realm' }
25
26
  #
26
- # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
27
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
27
28
  # htpasswd.set_passwd config[:Realm], 'username', 'password'
28
29
  # htpasswd.flush
29
30
  #
@@ -34,7 +35,7 @@ module WEBrick
34
35
  class BasicAuth
35
36
  include Authenticator
36
37
 
37
- AuthScheme = "Basic"
38
+ AuthScheme = "Basic" # :nodoc:
38
39
 
39
40
  ##
40
41
  # Used by UserDB to create a basic password entry
@@ -80,7 +81,15 @@ module WEBrick
80
81
  error("%s: the user is not allowed.", userid)
81
82
  challenge(req, res)
82
83
  end
83
- if password.crypt(encpass) != encpass
84
+
85
+ case encpass
86
+ when /\A\$2[aby]\$/
87
+ password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
88
+ else
89
+ password_matches = password.crypt(encpass) == encpass
90
+ end
91
+
92
+ unless password_matches
84
93
  error("%s: password unmatch.", userid)
85
94
  challenge(req, res)
86
95
  end
@@ -89,8 +98,7 @@ module WEBrick
89
98
  end
90
99
 
91
100
  ##
92
- # Returns a challenge response which asks for for authentication
93
- # information
101
+ # Returns a challenge response which asks for authentication information
94
102
 
95
103
  def challenge(req, res)
96
104
  res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/digestauth.rb -- HTTP digest access authentication
3
4
  #
@@ -11,9 +12,9 @@
11
12
  #
12
13
  # $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
13
14
 
14
- require 'webrick/config'
15
- require 'webrick/httpstatus'
16
- require 'webrick/httpauth/authenticator'
15
+ require_relative '../config'
16
+ require_relative '../httpstatus'
17
+ require_relative 'authenticator'
17
18
  require 'digest/md5'
18
19
  require 'digest/sha1'
19
20
 
@@ -45,9 +46,22 @@ module WEBrick
45
46
  class DigestAuth
46
47
  include Authenticator
47
48
 
48
- AuthScheme = "Digest"
49
- OpaqueInfo = Struct.new(:time, :nonce, :nc)
50
- attr_reader :algorithm, :qop
49
+ AuthScheme = "Digest" # :nodoc:
50
+
51
+ ##
52
+ # Struct containing the opaque portion of the digest authentication
53
+
54
+ OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
55
+
56
+ ##
57
+ # Digest authentication algorithm
58
+
59
+ attr_reader :algorithm
60
+
61
+ ##
62
+ # Quality of protection. RFC 2617 defines "auth" and "auth-int"
63
+
64
+ attr_reader :qop
51
65
 
52
66
  ##
53
67
  # Used by UserDB to create a digest password entry
@@ -97,7 +111,7 @@ module WEBrick
97
111
  @instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
98
112
  @opaques = {}
99
113
  @last_nonce_expire = Time.now
100
- @mutex = Mutex.new
114
+ @mutex = Thread::Mutex.new
101
115
  end
102
116
 
103
117
  ##
@@ -115,8 +129,7 @@ module WEBrick
115
129
  end
116
130
 
117
131
  ##
118
- # Returns a challenge response which asks for for authentication
119
- # information
132
+ # Returns a challenge response which asks for authentication information
120
133
 
121
134
  def challenge(req, res, stale=false)
122
135
  nonce = generate_next_nonce(req)
@@ -142,6 +155,8 @@ module WEBrick
142
155
 
143
156
  private
144
157
 
158
+ # :stopdoc:
159
+
145
160
  MustParams = ['username','realm','nonce','uri','response']
146
161
  MustParamsAuth = ['cnonce','nc']
147
162
 
@@ -189,7 +204,7 @@ module WEBrick
189
204
 
190
205
  password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
191
206
  unless password
192
- error('%s: the user is not allowd.', auth_req['username'])
207
+ error('%s: the user is not allowed.', auth_req['username'])
193
208
  return false
194
209
  end
195
210
 
@@ -220,9 +235,11 @@ module WEBrick
220
235
  ha2 = hexdigest(req.request_method, auth_req['uri'])
221
236
  ha2_res = hexdigest("", auth_req['uri'])
222
237
  elsif auth_req['qop'] == "auth-int"
223
- ha2 = hexdigest(req.request_method, auth_req['uri'],
224
- hexdigest(req.body))
225
- ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body))
238
+ body_digest = @h.new
239
+ req.body { |chunk| body_digest.update(chunk) }
240
+ body_digest = body_digest.hexdigest
241
+ ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
242
+ ha2_res = hexdigest("", auth_req['uri'], body_digest)
226
243
  end
227
244
 
228
245
  if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
@@ -273,23 +290,8 @@ module WEBrick
273
290
 
274
291
  def split_param_value(string)
275
292
  ret = {}
276
- while string.bytesize != 0
277
- case string
278
- when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
279
- key = $1
280
- matched = $2
281
- string = $'
282
- ret[key] = matched.gsub(/\\(.)/, "\\1")
283
- when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/
284
- key = $1
285
- matched = $2
286
- string = $'
287
- ret[key] = matched.clone
288
- when /^s*^,/
289
- string = $'
290
- else
291
- break
292
- end
293
+ string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
294
+ ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
293
295
  end
294
296
  ret
295
297
  end
@@ -297,7 +299,7 @@ module WEBrick
297
299
  def generate_next_nonce(req)
298
300
  now = "%012d" % req.request_time.to_i
299
301
  pk = hexdigest(now, @instance_key)[0,32]
300
- nonce = [now + ":" + pk].pack("m*").chop # it has 60 length of chars.
302
+ nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars.
301
303
  nonce
302
304
  end
303
305
 
@@ -375,6 +377,7 @@ module WEBrick
375
377
  @h.hexdigest(args.join(":"))
376
378
  end
377
379
 
380
+ # :startdoc:
378
381
  end
379
382
 
380
383
  ##
@@ -384,7 +387,7 @@ module WEBrick
384
387
  include ProxyAuthenticator
385
388
 
386
389
  private
387
- def check_uri(req, auth_req)
390
+ def check_uri(req, auth_req) # :nodoc:
388
391
  return true
389
392
  end
390
393
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/htdigest.rb -- Apache compatible htdigest file
3
4
  #
@@ -7,8 +8,8 @@
7
8
  #
8
9
  # $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
10
 
10
- require 'webrick/httpauth/userdb'
11
- require 'webrick/httpauth/digestauth'
11
+ require_relative 'userdb'
12
+ require_relative 'digestauth'
12
13
  require 'tempfile'
13
14
 
14
15
  module WEBrick
@@ -37,9 +38,9 @@ module WEBrick
37
38
  @path = path
38
39
  @mtime = Time.at(0)
39
40
  @digest = Hash.new
40
- @mutex = Mutex::new
41
+ @mutex = Thread::Mutex::new
41
42
  @auth_type = DigestAuth
42
- open(@path,"a").close unless File::exist?(@path)
43
+ File.open(@path,"a").close unless File.exist?(@path)
43
44
  reload
44
45
  end
45
46
 
@@ -50,7 +51,7 @@ module WEBrick
50
51
  mtime = File::mtime(@path)
51
52
  if mtime > @mtime
52
53
  @digest.clear
53
- open(@path){|io|
54
+ File.open(@path){|io|
54
55
  while line = io.gets
55
56
  line.chomp!
56
57
  user, realm, pass = line.split(/:/, 3)
@@ -70,13 +71,16 @@ module WEBrick
70
71
 
71
72
  def flush(output=nil)
72
73
  output ||= @path
73
- tmp = Tempfile.new("htpasswd", File::dirname(output))
74
+ tmp = Tempfile.create("htpasswd", File::dirname(output))
75
+ renamed = false
74
76
  begin
75
77
  each{|item| tmp.puts(item.join(":")) }
76
78
  tmp.close
77
79
  File::rename(tmp.path, output)
78
- rescue
79
- tmp.close(true)
80
+ renamed = true
81
+ ensure
82
+ tmp.close
83
+ File.unlink(tmp.path) if !renamed
80
84
  end
81
85
  end
82
86