webrick 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of webrick might be problematic. Click here for more details.
- 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/accesslog.rb +9 -1
- 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/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/httpauth.rb +6 -5
- data/lib/webrick/httpproxy.rb +93 -48
- data/lib/webrick/httprequest.rb +192 -27
- data/lib/webrick/httpresponse.rb +221 -70
- data/lib/webrick/https.rb +90 -2
- data/lib/webrick/httpserver.rb +45 -15
- data/lib/webrick/httpservlet/abstract.rb +5 -6
- data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
- data/lib/webrick/httpservlet/cgihandler.rb +22 -10
- 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/httpservlet.rb +6 -5
- data/lib/webrick/httpstatus.rb +24 -14
- data/lib/webrick/httputils.rb +133 -13
- 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/lib/webrick.rb +7 -7
- data/webrick.gemspec +76 -0
- metadata +70 -69
- 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
@@ -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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
50
|
-
|
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
|
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
|
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
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
277
|
-
|
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("
|
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
|
-
|
11
|
-
|
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
|
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.
|
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
|
-
|
79
|
-
|
80
|
+
renamed = true
|
81
|
+
ensure
|
82
|
+
tmp.close
|
83
|
+
File.unlink(tmp.path) if !renamed
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth/htgroup.rb -- Apache compatible htgroup file
|
3
4
|
#
|
@@ -35,7 +36,7 @@ module WEBrick
|
|
35
36
|
@path = path
|
36
37
|
@mtime = Time.at(0)
|
37
38
|
@group = Hash.new
|
38
|
-
open(@path,"a").close unless File
|
39
|
+
File.open(@path,"a").close unless File.exist?(@path)
|
39
40
|
reload
|
40
41
|
end
|
41
42
|
|
@@ -45,7 +46,7 @@ module WEBrick
|
|
45
46
|
def reload
|
46
47
|
if (mtime = File::mtime(@path)) > @mtime
|
47
48
|
@group.clear
|
48
|
-
open(@path){|io|
|
49
|
+
File.open(@path){|io|
|
49
50
|
while line = io.gets
|
50
51
|
line.chomp!
|
51
52
|
group, members = line.split(/:\s*/)
|
@@ -62,15 +63,18 @@ module WEBrick
|
|
62
63
|
|
63
64
|
def flush(output=nil)
|
64
65
|
output ||= @path
|
65
|
-
tmp = Tempfile.
|
66
|
+
tmp = Tempfile.create("htgroup", File::dirname(output))
|
66
67
|
begin
|
67
68
|
@group.keys.sort.each{|group|
|
68
69
|
tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
|
69
70
|
}
|
71
|
+
ensure
|
70
72
|
tmp.close
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
if $!
|
74
|
+
File.unlink(tmp.path)
|
75
|
+
else
|
76
|
+
return File.rename(tmp.path, output)
|
77
|
+
end
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth/htpasswd -- Apache compatible htpasswd file
|
3
4
|
#
|
@@ -7,8 +8,8 @@
|
|
7
8
|
#
|
8
9
|
# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
require_relative 'userdb'
|
12
|
+
require_relative 'basicauth'
|
12
13
|
require 'tempfile'
|
13
14
|
|
14
15
|
module WEBrick
|
@@ -34,12 +35,30 @@ module WEBrick
|
|
34
35
|
##
|
35
36
|
# Open a password database at +path+
|
36
37
|
|
37
|
-
def initialize(path)
|
38
|
+
def initialize(path, password_hash: nil)
|
38
39
|
@path = path
|
39
40
|
@mtime = Time.at(0)
|
40
41
|
@passwd = Hash.new
|
41
42
|
@auth_type = BasicAuth
|
42
|
-
|
43
|
+
@password_hash = password_hash
|
44
|
+
|
45
|
+
case @password_hash
|
46
|
+
when nil
|
47
|
+
# begin
|
48
|
+
# require "string/crypt"
|
49
|
+
# rescue LoadError
|
50
|
+
# warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
|
51
|
+
# end
|
52
|
+
@password_hash = :crypt
|
53
|
+
when :crypt
|
54
|
+
# require "string/crypt"
|
55
|
+
when :bcrypt
|
56
|
+
require "bcrypt"
|
57
|
+
else
|
58
|
+
raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
|
59
|
+
end
|
60
|
+
|
61
|
+
File.open(@path,"a").close unless File.exist?(@path)
|
43
62
|
reload
|
44
63
|
end
|
45
64
|
|
@@ -50,11 +69,19 @@ module WEBrick
|
|
50
69
|
mtime = File::mtime(@path)
|
51
70
|
if mtime > @mtime
|
52
71
|
@passwd.clear
|
53
|
-
open(@path){|io|
|
72
|
+
File.open(@path){|io|
|
54
73
|
while line = io.gets
|
55
74
|
line.chomp!
|
56
75
|
case line
|
57
76
|
when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
|
77
|
+
if @password_hash == :bcrypt
|
78
|
+
raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
|
79
|
+
end
|
80
|
+
user, pass = line.split(":")
|
81
|
+
when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
|
82
|
+
if @password_hash == :crypt
|
83
|
+
raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
|
84
|
+
end
|
58
85
|
user, pass = line.split(":")
|
59
86
|
when /:\$/, /:{SHA}/
|
60
87
|
raise NotImplementedError,
|
@@ -75,13 +102,16 @@ module WEBrick
|
|
75
102
|
|
76
103
|
def flush(output=nil)
|
77
104
|
output ||= @path
|
78
|
-
tmp = Tempfile.
|
105
|
+
tmp = Tempfile.create("htpasswd", File::dirname(output))
|
106
|
+
renamed = false
|
79
107
|
begin
|
80
108
|
each{|item| tmp.puts(item.join(":")) }
|
81
109
|
tmp.close
|
82
110
|
File::rename(tmp.path, output)
|
83
|
-
|
84
|
-
|
111
|
+
renamed = true
|
112
|
+
ensure
|
113
|
+
tmp.close
|
114
|
+
File.unlink(tmp.path) if !renamed
|
85
115
|
end
|
86
116
|
end
|
87
117
|
|
@@ -98,7 +128,14 @@ module WEBrick
|
|
98
128
|
# Sets a password in the database for +user+ in +realm+ to +pass+.
|
99
129
|
|
100
130
|
def set_passwd(realm, user, pass)
|
101
|
-
@
|
131
|
+
if @password_hash == :bcrypt
|
132
|
+
# Cost of 5 to match Apache default, and because the
|
133
|
+
# bcrypt default of 10 will introduce significant delays
|
134
|
+
# for every request.
|
135
|
+
@passwd[user] = BCrypt::Password.create(pass, :cost=>5)
|
136
|
+
else
|
137
|
+
@passwd[user] = make_passwd(realm, user, pass)
|
138
|
+
end
|
102
139
|
end
|
103
140
|
|
104
141
|
##
|
data/lib/webrick/httpauth.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
|
data/lib/webrick/httpproxy.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpproxy.rb -- HTTPProxy Class
|
3
4
|
#
|
@@ -9,22 +10,21 @@
|
|
9
10
|
# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
|
10
11
|
# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
|
11
12
|
|
12
|
-
|
13
|
+
require_relative "httpserver"
|
13
14
|
require "net/http"
|
14
15
|
|
15
|
-
Net::HTTP::version_1_2 if RUBY_VERSION < "1.7"
|
16
|
-
|
17
16
|
module WEBrick
|
18
|
-
|
19
|
-
|
17
|
+
|
18
|
+
NullReader = Object.new # :nodoc:
|
19
|
+
class << NullReader # :nodoc:
|
20
20
|
def read(*args)
|
21
21
|
nil
|
22
22
|
end
|
23
23
|
alias gets read
|
24
24
|
end
|
25
25
|
|
26
|
-
FakeProxyURI = Object.new
|
27
|
-
class << FakeProxyURI
|
26
|
+
FakeProxyURI = Object.new # :nodoc:
|
27
|
+
class << FakeProxyURI # :nodoc:
|
28
28
|
def method_missing(meth, *args)
|
29
29
|
if %w(scheme host port path query userinfo).member?(meth.to_s)
|
30
30
|
return nil
|
@@ -33,8 +33,38 @@ module WEBrick
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
# :startdoc:
|
37
|
+
|
36
38
|
##
|
37
39
|
# An HTTP Proxy server which proxies GET, HEAD and POST requests.
|
40
|
+
#
|
41
|
+
# To create a simple proxy server:
|
42
|
+
#
|
43
|
+
# require 'webrick'
|
44
|
+
# require 'webrick/httpproxy'
|
45
|
+
#
|
46
|
+
# proxy = WEBrick::HTTPProxyServer.new Port: 8000
|
47
|
+
#
|
48
|
+
# trap 'INT' do proxy.shutdown end
|
49
|
+
# trap 'TERM' do proxy.shutdown end
|
50
|
+
#
|
51
|
+
# proxy.start
|
52
|
+
#
|
53
|
+
# See ::new for proxy-specific configuration items.
|
54
|
+
#
|
55
|
+
# == Modifying proxied responses
|
56
|
+
#
|
57
|
+
# To modify content the proxy server returns use the +:ProxyContentHandler+
|
58
|
+
# option:
|
59
|
+
#
|
60
|
+
# handler = proc do |req, res|
|
61
|
+
# if res['content-type'] == 'text/plain' then
|
62
|
+
# res.body << "\nThis content was proxied!\n"
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# proxy =
|
67
|
+
# WEBrick::HTTPProxyServer.new Port: 8000, ProxyContentHandler: handler
|
38
68
|
|
39
69
|
class HTTPProxyServer < HTTPServer
|
40
70
|
|
@@ -46,7 +76,7 @@ module WEBrick
|
|
46
76
|
# request
|
47
77
|
# :ProxyVia:: Appended to the via header
|
48
78
|
# :ProxyURI:: The proxy server's URI
|
49
|
-
# :ProxyContentHandler:: Called with a request and
|
79
|
+
# :ProxyContentHandler:: Called with a request and response and allows
|
50
80
|
# modification of the response
|
51
81
|
# :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60
|
52
82
|
# seconds for read operations
|
@@ -57,6 +87,7 @@ module WEBrick
|
|
57
87
|
@via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
|
58
88
|
end
|
59
89
|
|
90
|
+
# :stopdoc:
|
60
91
|
def service(req, res)
|
61
92
|
if req.request_method == "CONNECT"
|
62
93
|
do_CONNECT(req, res)
|
@@ -112,7 +143,7 @@ module WEBrick
|
|
112
143
|
if proxy = proxy_uri(req, res)
|
113
144
|
proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
|
114
145
|
if proxy.userinfo
|
115
|
-
credentials = "Basic " + [proxy.userinfo].pack("
|
146
|
+
credentials = "Basic " + [proxy.userinfo].pack("m0")
|
116
147
|
end
|
117
148
|
host, port = proxy.host, proxy.port
|
118
149
|
end
|
@@ -126,12 +157,12 @@ module WEBrick
|
|
126
157
|
os << proxy_request_line << CRLF
|
127
158
|
@logger.debug("CONNECT: > #{proxy_request_line}")
|
128
159
|
if credentials
|
129
|
-
@logger.debug("CONNECT: sending
|
160
|
+
@logger.debug("CONNECT: sending credentials")
|
130
161
|
os << "Proxy-Authorization: " << credentials << CRLF
|
131
162
|
end
|
132
163
|
os << CRLF
|
133
164
|
proxy_status_line = os.gets(LF)
|
134
|
-
@logger.debug("CONNECT: read
|
165
|
+
@logger.debug("CONNECT: read Status-Line from the upstream server")
|
135
166
|
@logger.debug("CONNECT: < #{proxy_status_line}")
|
136
167
|
if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
|
137
168
|
while line = os.gets(LF)
|
@@ -154,7 +185,7 @@ module WEBrick
|
|
154
185
|
res.send_response(ua)
|
155
186
|
access_log(@config, req, res)
|
156
187
|
|
157
|
-
# Should clear request-line not to send the
|
188
|
+
# Should clear request-line not to send the response twice.
|
158
189
|
# see: HTTPServer#run
|
159
190
|
req.parse(NullReader) rescue nil
|
160
191
|
end
|
@@ -162,16 +193,16 @@ module WEBrick
|
|
162
193
|
begin
|
163
194
|
while fds = IO::select([ua, os])
|
164
195
|
if fds[0].member?(ua)
|
165
|
-
buf = ua.
|
196
|
+
buf = ua.readpartial(1024);
|
166
197
|
@logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
|
167
|
-
os.
|
198
|
+
os.write(buf)
|
168
199
|
elsif fds[0].member?(os)
|
169
|
-
buf = os.
|
200
|
+
buf = os.readpartial(1024);
|
170
201
|
@logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
|
171
|
-
ua.
|
202
|
+
ua.write(buf)
|
172
203
|
end
|
173
204
|
end
|
174
|
-
rescue
|
205
|
+
rescue
|
175
206
|
os.close
|
176
207
|
@logger.debug("CONNECT #{host}:#{port}: closed")
|
177
208
|
end
|
@@ -180,21 +211,15 @@ module WEBrick
|
|
180
211
|
end
|
181
212
|
|
182
213
|
def do_GET(req, res)
|
183
|
-
perform_proxy_request(req, res
|
184
|
-
http.get(path, header)
|
185
|
-
end
|
214
|
+
perform_proxy_request(req, res, Net::HTTP::Get)
|
186
215
|
end
|
187
216
|
|
188
217
|
def do_HEAD(req, res)
|
189
|
-
perform_proxy_request(req, res
|
190
|
-
http.head(path, header)
|
191
|
-
end
|
218
|
+
perform_proxy_request(req, res, Net::HTTP::Head)
|
192
219
|
end
|
193
220
|
|
194
221
|
def do_POST(req, res)
|
195
|
-
perform_proxy_request(req, res
|
196
|
-
http.post(path, req.body || "", header)
|
197
|
-
end
|
222
|
+
perform_proxy_request(req, res, Net::HTTP::Post, req.body_reader)
|
198
223
|
end
|
199
224
|
|
200
225
|
def do_OPTIONS(req, res)
|
@@ -263,43 +288,63 @@ module WEBrick
|
|
263
288
|
if upstream = proxy_uri(req, res)
|
264
289
|
if upstream.userinfo
|
265
290
|
header['proxy-authorization'] =
|
266
|
-
"Basic " + [upstream.userinfo].pack("
|
291
|
+
"Basic " + [upstream.userinfo].pack("m0")
|
267
292
|
end
|
268
293
|
return upstream
|
269
294
|
end
|
270
295
|
return FakeProxyURI
|
271
296
|
end
|
272
297
|
|
273
|
-
def perform_proxy_request(req, res)
|
298
|
+
def perform_proxy_request(req, res, req_class, body_stream = nil)
|
274
299
|
uri = req.request_uri
|
275
300
|
path = uri.path.dup
|
276
301
|
path << "?" << uri.query if uri.query
|
277
302
|
header = setup_proxy_header(req, res)
|
278
303
|
upstream = setup_upstream_proxy_authentication(req, res, header)
|
279
|
-
response = nil
|
280
304
|
|
305
|
+
body_tmp = []
|
281
306
|
http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
307
|
+
req_fib = Fiber.new do
|
308
|
+
http.start do
|
309
|
+
if @config[:ProxyTimeout]
|
310
|
+
################################## these issues are
|
311
|
+
http.open_timeout = 30 # secs # necessary (maybe because
|
312
|
+
http.read_timeout = 60 # secs # Ruby's bug, but why?)
|
313
|
+
##################################
|
314
|
+
end
|
315
|
+
if body_stream && req['transfer-encoding'] =~ /\bchunked\b/i
|
316
|
+
header['Transfer-Encoding'] = 'chunked'
|
317
|
+
end
|
318
|
+
http_req = req_class.new(path, header)
|
319
|
+
http_req.body_stream = body_stream if body_stream
|
320
|
+
http.request(http_req) do |response|
|
321
|
+
# Persistent connection requirements are mysterious for me.
|
322
|
+
# So I will close the connection in every response.
|
323
|
+
res['proxy-connection'] = "close"
|
324
|
+
res['connection'] = "close"
|
325
|
+
|
326
|
+
# stream Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
|
327
|
+
res.status = response.code.to_i
|
328
|
+
res.chunked = response.chunked?
|
329
|
+
choose_header(response, res)
|
330
|
+
set_cookie(response, res)
|
331
|
+
set_via(res)
|
332
|
+
response.read_body do |buf|
|
333
|
+
body_tmp << buf
|
334
|
+
Fiber.yield # wait for res.body Proc#call
|
335
|
+
end
|
336
|
+
end # http.request
|
337
|
+
end
|
338
|
+
end
|
339
|
+
req_fib.resume # read HTTP response headers and first chunk of the body
|
340
|
+
res.body = ->(socket) do
|
341
|
+
while buf = body_tmp.shift
|
342
|
+
socket.write(buf)
|
343
|
+
buf.clear
|
344
|
+
req_fib.resume # continue response.read_body
|
288
345
|
end
|
289
|
-
response = yield(http, path, header)
|
290
346
|
end
|
291
|
-
|
292
|
-
# Persistent connection requirements are mysterious for me.
|
293
|
-
# So I will close the connection in every response.
|
294
|
-
res['proxy-connection'] = "close"
|
295
|
-
res['connection'] = "close"
|
296
|
-
|
297
|
-
# Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
|
298
|
-
res.status = response.code.to_i
|
299
|
-
choose_header(response, res)
|
300
|
-
set_cookie(response, res)
|
301
|
-
set_via(res)
|
302
|
-
res.body = response.body
|
303
347
|
end
|
348
|
+
# :stopdoc:
|
304
349
|
end
|
305
350
|
end
|