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.

Files changed (63) hide show
  1. data/README.txt +21 -0
  2. data/lib/webrick.rb +227 -0
  3. data/lib/webrick/accesslog.rb +151 -0
  4. data/lib/webrick/cgi.rb +260 -0
  5. data/lib/webrick/compat.rb +35 -0
  6. data/lib/webrick/config.rb +121 -0
  7. data/lib/webrick/cookie.rb +110 -0
  8. data/lib/webrick/htmlutils.rb +28 -0
  9. data/lib/webrick/httpauth.rb +95 -0
  10. data/lib/webrick/httpauth/authenticator.rb +112 -0
  11. data/lib/webrick/httpauth/basicauth.rb +108 -0
  12. data/lib/webrick/httpauth/digestauth.rb +392 -0
  13. data/lib/webrick/httpauth/htdigest.rb +128 -0
  14. data/lib/webrick/httpauth/htgroup.rb +93 -0
  15. data/lib/webrick/httpauth/htpasswd.rb +121 -0
  16. data/lib/webrick/httpauth/userdb.rb +52 -0
  17. data/lib/webrick/httpproxy.rb +305 -0
  18. data/lib/webrick/httprequest.rb +461 -0
  19. data/lib/webrick/httpresponse.rb +399 -0
  20. data/lib/webrick/https.rb +64 -0
  21. data/lib/webrick/httpserver.rb +264 -0
  22. data/lib/webrick/httpservlet.rb +22 -0
  23. data/lib/webrick/httpservlet/abstract.rb +153 -0
  24. data/lib/webrick/httpservlet/cgi_runner.rb +46 -0
  25. data/lib/webrick/httpservlet/cgihandler.rb +108 -0
  26. data/lib/webrick/httpservlet/erbhandler.rb +87 -0
  27. data/lib/webrick/httpservlet/filehandler.rb +470 -0
  28. data/lib/webrick/httpservlet/prochandler.rb +33 -0
  29. data/lib/webrick/httpstatus.rb +184 -0
  30. data/lib/webrick/httputils.rb +394 -0
  31. data/lib/webrick/httpversion.rb +49 -0
  32. data/lib/webrick/log.rb +136 -0
  33. data/lib/webrick/server.rb +218 -0
  34. data/lib/webrick/ssl.rb +127 -0
  35. data/lib/webrick/utils.rb +241 -0
  36. data/lib/webrick/version.rb +13 -0
  37. data/sample/webrick/demo-app.rb +66 -0
  38. data/sample/webrick/demo-multipart.cgi +12 -0
  39. data/sample/webrick/demo-servlet.rb +6 -0
  40. data/sample/webrick/demo-urlencoded.cgi +12 -0
  41. data/sample/webrick/hello.cgi +11 -0
  42. data/sample/webrick/hello.rb +8 -0
  43. data/sample/webrick/httpd.rb +23 -0
  44. data/sample/webrick/httpproxy.rb +25 -0
  45. data/sample/webrick/httpsd.rb +33 -0
  46. data/test/openssl/utils.rb +313 -0
  47. data/test/ruby/envutil.rb +208 -0
  48. data/test/webrick/test_cgi.rb +134 -0
  49. data/test/webrick/test_cookie.rb +131 -0
  50. data/test/webrick/test_filehandler.rb +285 -0
  51. data/test/webrick/test_httpauth.rb +167 -0
  52. data/test/webrick/test_httpproxy.rb +282 -0
  53. data/test/webrick/test_httprequest.rb +411 -0
  54. data/test/webrick/test_httpresponse.rb +49 -0
  55. data/test/webrick/test_httpserver.rb +305 -0
  56. data/test/webrick/test_httputils.rb +96 -0
  57. data/test/webrick/test_httpversion.rb +40 -0
  58. data/test/webrick/test_server.rb +67 -0
  59. data/test/webrick/test_utils.rb +64 -0
  60. data/test/webrick/utils.rb +58 -0
  61. data/test/webrick/webrick.cgi +36 -0
  62. data/test/webrick/webrick_long_filename.cgi +36 -0
  63. metadata +106 -0
@@ -0,0 +1,128 @@
1
+ #
2
+ # httpauth/htdigest.rb -- Apache compatible htdigest file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
+
10
+ require 'webrick/httpauth/userdb'
11
+ require 'webrick/httpauth/digestauth'
12
+ require 'tempfile'
13
+
14
+ module WEBrick
15
+ module HTTPAuth
16
+
17
+ ##
18
+ # Htdigest accesses apache-compatible digest password files. Passwords are
19
+ # matched to a realm where they are valid. For security, the path for a
20
+ # digest password database should be stored outside of the paths available
21
+ # to the HTTP server.
22
+ #
23
+ # Htdigest is intended for use with WEBrick::HTTPAuth::DigestAuth and
24
+ # stores passwords using cryptographic hashes.
25
+ #
26
+ # htpasswd = WEBrick::HTTPAuth::Htdigest.new 'my_password_file'
27
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
28
+ # htpasswd.flush
29
+
30
+ class Htdigest
31
+ include UserDB
32
+
33
+ ##
34
+ # Open a digest password database at +path+
35
+
36
+ def initialize(path)
37
+ @path = path
38
+ @mtime = Time.at(0)
39
+ @digest = Hash.new
40
+ @mutex = Mutex::new
41
+ @auth_type = DigestAuth
42
+ open(@path,"a").close unless File::exist?(@path)
43
+ reload
44
+ end
45
+
46
+ ##
47
+ # Reloads passwords from the database
48
+
49
+ def reload
50
+ mtime = File::mtime(@path)
51
+ if mtime > @mtime
52
+ @digest.clear
53
+ open(@path){|io|
54
+ while line = io.gets
55
+ line.chomp!
56
+ user, realm, pass = line.split(/:/, 3)
57
+ unless @digest[realm]
58
+ @digest[realm] = Hash.new
59
+ end
60
+ @digest[realm][user] = pass
61
+ end
62
+ }
63
+ @mtime = mtime
64
+ end
65
+ end
66
+
67
+ ##
68
+ # Flush the password database. If +output+ is given the database will
69
+ # be written there instead of to the original path.
70
+
71
+ def flush(output=nil)
72
+ output ||= @path
73
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
74
+ begin
75
+ each{|item| tmp.puts(item.join(":")) }
76
+ tmp.close
77
+ File::rename(tmp.path, output)
78
+ rescue
79
+ tmp.close(true)
80
+ end
81
+ end
82
+
83
+ ##
84
+ # Retrieves a password from the database for +user+ in +realm+. If
85
+ # +reload_db+ is true the database will be reloaded first.
86
+
87
+ def get_passwd(realm, user, reload_db)
88
+ reload() if reload_db
89
+ if hash = @digest[realm]
90
+ hash[user]
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
96
+
97
+ def set_passwd(realm, user, pass)
98
+ @mutex.synchronize{
99
+ unless @digest[realm]
100
+ @digest[realm] = Hash.new
101
+ end
102
+ @digest[realm][user] = make_passwd(realm, user, pass)
103
+ }
104
+ end
105
+
106
+ ##
107
+ # Removes a password from the database for +user+ in +realm+.
108
+
109
+ def delete_passwd(realm, user)
110
+ if hash = @digest[realm]
111
+ hash.delete(user)
112
+ end
113
+ end
114
+
115
+ ##
116
+ # Iterate passwords in the database.
117
+
118
+ def each # :yields: [user, realm, password_hash]
119
+ @digest.keys.sort.each{|realm|
120
+ hash = @digest[realm]
121
+ hash.keys.sort.each{|user|
122
+ yield([user, realm, hash[user]])
123
+ }
124
+ }
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,93 @@
1
+ #
2
+ # httpauth/htgroup.rb -- Apache compatible htgroup file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $
9
+
10
+ require 'tempfile'
11
+
12
+ module WEBrick
13
+ module HTTPAuth
14
+
15
+ ##
16
+ # Htgroup accesses apache-compatible group files. Htgroup can be used to
17
+ # provide group-based authentication for users. Currently Htgroup is not
18
+ # directly integrated with any authenticators in WEBrick. For security,
19
+ # the path for a digest password database should be stored outside of the
20
+ # paths available to the HTTP server.
21
+ #
22
+ # Example:
23
+ #
24
+ # htgroup = WEBrick::HTTPAuth::Htgroup.new 'my_group_file'
25
+ # htgroup.add 'superheroes', %w[spiderman batman]
26
+ #
27
+ # htgroup.members('superheroes').include? 'magneto' # => false
28
+
29
+ class Htgroup
30
+
31
+ ##
32
+ # Open a group database at +path+
33
+
34
+ def initialize(path)
35
+ @path = path
36
+ @mtime = Time.at(0)
37
+ @group = Hash.new
38
+ open(@path,"a").close unless File::exist?(@path)
39
+ reload
40
+ end
41
+
42
+ ##
43
+ # Reload groups from the database
44
+
45
+ def reload
46
+ if (mtime = File::mtime(@path)) > @mtime
47
+ @group.clear
48
+ open(@path){|io|
49
+ while line = io.gets
50
+ line.chomp!
51
+ group, members = line.split(/:\s*/)
52
+ @group[group] = members.split(/\s+/)
53
+ end
54
+ }
55
+ @mtime = mtime
56
+ end
57
+ end
58
+
59
+ ##
60
+ # Flush the group database. If +output+ is given the database will be
61
+ # written there instead of to the original path.
62
+
63
+ def flush(output=nil)
64
+ output ||= @path
65
+ tmp = Tempfile.new("htgroup", File::dirname(output))
66
+ begin
67
+ @group.keys.sort.each{|group|
68
+ tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
69
+ }
70
+ tmp.close
71
+ File::rename(tmp.path, output)
72
+ rescue
73
+ tmp.close(true)
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Retrieve the list of members from +group+
79
+
80
+ def members(group)
81
+ reload
82
+ @group[group] || []
83
+ end
84
+
85
+ ##
86
+ # Add an Array of +members+ to +group+
87
+
88
+ def add(group, members)
89
+ @group[group] = members(group) | members
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,121 @@
1
+ #
2
+ # httpauth/htpasswd -- Apache compatible htpasswd file
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
+
10
+ require 'webrick/httpauth/userdb'
11
+ require 'webrick/httpauth/basicauth'
12
+ require 'tempfile'
13
+
14
+ module WEBrick
15
+ module HTTPAuth
16
+
17
+ ##
18
+ # Htpasswd accesses apache-compatible password files. Passwords are
19
+ # matched to a realm where they are valid. For security, the path for a
20
+ # password database should be stored outside of the paths available to the
21
+ # HTTP server.
22
+ #
23
+ # Htpasswd is intended for use with WEBrick::HTTPAuth::BasicAuth.
24
+ #
25
+ # To create an Htpasswd database with a single user:
26
+ #
27
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
28
+ # htpasswd.set_passwd 'my realm', 'username', 'password'
29
+ # htpasswd.flush
30
+
31
+ class Htpasswd
32
+ include UserDB
33
+
34
+ ##
35
+ # Open a password database at +path+
36
+
37
+ def initialize(path)
38
+ @path = path
39
+ @mtime = Time.at(0)
40
+ @passwd = Hash.new
41
+ @auth_type = BasicAuth
42
+ open(@path,"a").close unless File::exist?(@path)
43
+ reload
44
+ end
45
+
46
+ ##
47
+ # Reload passwords from the database
48
+
49
+ def reload
50
+ mtime = File::mtime(@path)
51
+ if mtime > @mtime
52
+ @passwd.clear
53
+ open(@path){|io|
54
+ while line = io.gets
55
+ line.chomp!
56
+ case line
57
+ when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
58
+ user, pass = line.split(":")
59
+ when /:\$/, /:{SHA}/
60
+ raise NotImplementedError,
61
+ 'MD5, SHA1 .htpasswd file not supported'
62
+ else
63
+ raise StandardError, 'bad .htpasswd file'
64
+ end
65
+ @passwd[user] = pass
66
+ end
67
+ }
68
+ @mtime = mtime
69
+ end
70
+ end
71
+
72
+ ##
73
+ # Flush the password database. If +output+ is given the database will
74
+ # be written there instead of to the original path.
75
+
76
+ def flush(output=nil)
77
+ output ||= @path
78
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
79
+ begin
80
+ each{|item| tmp.puts(item.join(":")) }
81
+ tmp.close
82
+ File::rename(tmp.path, output)
83
+ rescue
84
+ tmp.close(true)
85
+ end
86
+ end
87
+
88
+ ##
89
+ # Retrieves a password from the database for +user+ in +realm+. If
90
+ # +reload_db+ is true the database will be reloaded first.
91
+
92
+ def get_passwd(realm, user, reload_db)
93
+ reload() if reload_db
94
+ @passwd[user]
95
+ end
96
+
97
+ ##
98
+ # Sets a password in the database for +user+ in +realm+ to +pass+.
99
+
100
+ def set_passwd(realm, user, pass)
101
+ @passwd[user] = make_passwd(realm, user, pass)
102
+ end
103
+
104
+ ##
105
+ # Removes a password from the database for +user+ in +realm+.
106
+
107
+ def delete_passwd(realm, user)
108
+ @passwd.delete(user)
109
+ end
110
+
111
+ ##
112
+ # Iterate passwords in the database.
113
+
114
+ def each # :yields: [user, password]
115
+ @passwd.keys.sort.each{|user|
116
+ yield([user, @passwd[user]])
117
+ }
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,52 @@
1
+ #--
2
+ # httpauth/userdb.rb -- UserDB mix-in module.
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6
+ # reserved.
7
+ #
8
+ # $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $
9
+
10
+ module WEBrick
11
+ module HTTPAuth
12
+
13
+ ##
14
+ # User database mixin for HTTPAuth. This mixin dispatches user record
15
+ # access to the underlying auth_type for this database.
16
+
17
+ module UserDB
18
+
19
+ ##
20
+ # The authentication type.
21
+ #
22
+ # WEBrick::HTTPAuth::BasicAuth or WEBrick::HTTPAuth::DigestAuth are
23
+ # built-in.
24
+
25
+ attr_accessor :auth_type
26
+
27
+ ##
28
+ # Creates an obscured password in +realm+ with +user+ and +password+
29
+ # using the auth_type of this database.
30
+
31
+ def make_passwd(realm, user, pass)
32
+ @auth_type::make_passwd(realm, user, pass)
33
+ end
34
+
35
+ ##
36
+ # Sets a password in +realm+ with +user+ and +password+ for the
37
+ # auth_type of this database.
38
+
39
+ def set_passwd(realm, user, pass)
40
+ self[user] = pass
41
+ end
42
+
43
+ ##
44
+ # Retrieves a password in +realm+ for +user+ for the auth_type of this
45
+ # database. +reload_db+ is a dummy value.
46
+
47
+ def get_passwd(realm, user, reload_db=false)
48
+ make_passwd(realm, user, self[user])
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,305 @@
1
+ #
2
+ # httpproxy.rb -- HTTPProxy Class
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2002 GOTO Kentaro
6
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
10
+ # $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
11
+
12
+ require "webrick/httpserver"
13
+ require "net/http"
14
+
15
+ Net::HTTP::version_1_2 if RUBY_VERSION < "1.7"
16
+
17
+ module WEBrick
18
+ NullReader = Object.new
19
+ class << NullReader
20
+ def read(*args)
21
+ nil
22
+ end
23
+ alias gets read
24
+ end
25
+
26
+ FakeProxyURI = Object.new
27
+ class << FakeProxyURI
28
+ def method_missing(meth, *args)
29
+ if %w(scheme host port path query userinfo).member?(meth.to_s)
30
+ return nil
31
+ end
32
+ super
33
+ end
34
+ end
35
+
36
+ ##
37
+ # An HTTP Proxy server which proxies GET, HEAD and POST requests.
38
+
39
+ class HTTPProxyServer < HTTPServer
40
+
41
+ ##
42
+ # Proxy server configurations. The proxy server handles the following
43
+ # configuration items in addition to those supported by HTTPServer:
44
+ #
45
+ # :ProxyAuthProc:: Called with a request and response to authorize a
46
+ # request
47
+ # :ProxyVia:: Appended to the via header
48
+ # :ProxyURI:: The proxy server's URI
49
+ # :ProxyContentHandler:: Called with a request and resopnse and allows
50
+ # modification of the response
51
+ # :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60
52
+ # seconds for read operations
53
+
54
+ def initialize(config={}, default=Config::HTTP)
55
+ super(config, default)
56
+ c = @config
57
+ @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
58
+ end
59
+
60
+ def service(req, res)
61
+ if req.request_method == "CONNECT"
62
+ do_CONNECT(req, res)
63
+ elsif req.unparsed_uri =~ %r!^http://!
64
+ proxy_service(req, res)
65
+ else
66
+ super(req, res)
67
+ end
68
+ end
69
+
70
+ def proxy_auth(req, res)
71
+ if proc = @config[:ProxyAuthProc]
72
+ proc.call(req, res)
73
+ end
74
+ req.header.delete("proxy-authorization")
75
+ end
76
+
77
+ def proxy_uri(req, res)
78
+ # should return upstream proxy server's URI
79
+ return @config[:ProxyURI]
80
+ end
81
+
82
+ def proxy_service(req, res)
83
+ # Proxy Authentication
84
+ proxy_auth(req, res)
85
+
86
+ begin
87
+ self.send("do_#{req.request_method}", req, res)
88
+ rescue NoMethodError
89
+ raise HTTPStatus::MethodNotAllowed,
90
+ "unsupported method `#{req.request_method}'."
91
+ rescue => err
92
+ logger.debug("#{err.class}: #{err.message}")
93
+ raise HTTPStatus::ServiceUnavailable, err.message
94
+ end
95
+
96
+ # Process contents
97
+ if handler = @config[:ProxyContentHandler]
98
+ handler.call(req, res)
99
+ end
100
+ end
101
+
102
+ def do_CONNECT(req, res)
103
+ # Proxy Authentication
104
+ proxy_auth(req, res)
105
+
106
+ ua = Thread.current[:WEBrickSocket] # User-Agent
107
+ raise HTTPStatus::InternalServerError,
108
+ "[BUG] cannot get socket" unless ua
109
+
110
+ host, port = req.unparsed_uri.split(":", 2)
111
+ # Proxy authentication for upstream proxy server
112
+ if proxy = proxy_uri(req, res)
113
+ proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
114
+ if proxy.userinfo
115
+ credentials = "Basic " + [proxy.userinfo].pack("m").delete("\n")
116
+ end
117
+ host, port = proxy.host, proxy.port
118
+ end
119
+
120
+ begin
121
+ @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
122
+ os = TCPSocket.new(host, port) # origin server
123
+
124
+ if proxy
125
+ @logger.debug("CONNECT: sending a Request-Line")
126
+ os << proxy_request_line << CRLF
127
+ @logger.debug("CONNECT: > #{proxy_request_line}")
128
+ if credentials
129
+ @logger.debug("CONNECT: sending a credentials")
130
+ os << "Proxy-Authorization: " << credentials << CRLF
131
+ end
132
+ os << CRLF
133
+ proxy_status_line = os.gets(LF)
134
+ @logger.debug("CONNECT: read a Status-Line form the upstream server")
135
+ @logger.debug("CONNECT: < #{proxy_status_line}")
136
+ if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
137
+ while line = os.gets(LF)
138
+ break if /\A(#{CRLF}|#{LF})\z/om =~ line
139
+ end
140
+ else
141
+ raise HTTPStatus::BadGateway
142
+ end
143
+ end
144
+ @logger.debug("CONNECT #{host}:#{port}: succeeded")
145
+ res.status = HTTPStatus::RC_OK
146
+ rescue => ex
147
+ @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
148
+ res.set_error(ex)
149
+ raise HTTPStatus::EOFError
150
+ ensure
151
+ if handler = @config[:ProxyContentHandler]
152
+ handler.call(req, res)
153
+ end
154
+ res.send_response(ua)
155
+ access_log(@config, req, res)
156
+
157
+ # Should clear request-line not to send the sesponse twice.
158
+ # see: HTTPServer#run
159
+ req.parse(NullReader) rescue nil
160
+ end
161
+
162
+ begin
163
+ while fds = IO::select([ua, os])
164
+ if fds[0].member?(ua)
165
+ buf = ua.sysread(1024);
166
+ @logger.debug("CONNECT: #{buf.bytesize} byte from User-Agent")
167
+ os.syswrite(buf)
168
+ elsif fds[0].member?(os)
169
+ buf = os.sysread(1024);
170
+ @logger.debug("CONNECT: #{buf.bytesize} byte from #{host}:#{port}")
171
+ ua.syswrite(buf)
172
+ end
173
+ end
174
+ rescue => ex
175
+ os.close
176
+ @logger.debug("CONNECT #{host}:#{port}: closed")
177
+ end
178
+
179
+ raise HTTPStatus::EOFError
180
+ end
181
+
182
+ def do_GET(req, res)
183
+ perform_proxy_request(req, res) do |http, path, header|
184
+ http.get(path, header)
185
+ end
186
+ end
187
+
188
+ def do_HEAD(req, res)
189
+ perform_proxy_request(req, res) do |http, path, header|
190
+ http.head(path, header)
191
+ end
192
+ end
193
+
194
+ def do_POST(req, res)
195
+ perform_proxy_request(req, res) do |http, path, header|
196
+ http.post(path, req.body || "", header)
197
+ end
198
+ end
199
+
200
+ def do_OPTIONS(req, res)
201
+ res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
202
+ end
203
+
204
+ private
205
+
206
+ # Some header fields should not be transferred.
207
+ HopByHop = %w( connection keep-alive proxy-authenticate upgrade
208
+ proxy-authorization te trailers transfer-encoding )
209
+ ShouldNotTransfer = %w( set-cookie proxy-connection )
210
+ def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
211
+
212
+ def choose_header(src, dst)
213
+ connections = split_field(src['connection'])
214
+ src.each{|key, value|
215
+ key = key.downcase
216
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
217
+ connections.member?(key) || # RFC2616: 14.10
218
+ ShouldNotTransfer.member?(key) # pragmatics
219
+ @logger.debug("choose_header: `#{key}: #{value}'")
220
+ next
221
+ end
222
+ dst[key] = value
223
+ }
224
+ end
225
+
226
+ # Net::HTTP is stupid about the multiple header fields.
227
+ # Here is workaround:
228
+ def set_cookie(src, dst)
229
+ if str = src['set-cookie']
230
+ cookies = []
231
+ str.split(/,\s*/).each{|token|
232
+ if /^[^=]+;/o =~ token
233
+ cookies[-1] << ", " << token
234
+ elsif /=/o =~ token
235
+ cookies << token
236
+ else
237
+ cookies[-1] << ", " << token
238
+ end
239
+ }
240
+ dst.cookies.replace(cookies)
241
+ end
242
+ end
243
+
244
+ def set_via(h)
245
+ if @config[:ProxyVia]
246
+ if h['via']
247
+ h['via'] << ", " << @via
248
+ else
249
+ h['via'] = @via
250
+ end
251
+ end
252
+ end
253
+
254
+ def setup_proxy_header(req, res)
255
+ # Choose header fields to transfer
256
+ header = Hash.new
257
+ choose_header(req, header)
258
+ set_via(header)
259
+ return header
260
+ end
261
+
262
+ def setup_upstream_proxy_authentication(req, res, header)
263
+ if upstream = proxy_uri(req, res)
264
+ if upstream.userinfo
265
+ header['proxy-authorization'] =
266
+ "Basic " + [upstream.userinfo].pack("m").delete("\n")
267
+ end
268
+ return upstream
269
+ end
270
+ return FakeProxyURI
271
+ end
272
+
273
+ def perform_proxy_request(req, res)
274
+ uri = req.request_uri
275
+ path = uri.path.dup
276
+ path << "?" << uri.query if uri.query
277
+ header = setup_proxy_header(req, res)
278
+ upstream = setup_upstream_proxy_authentication(req, res, header)
279
+ response = nil
280
+
281
+ http = Net::HTTP.new(uri.host, uri.port, upstream.host, upstream.port)
282
+ http.start do
283
+ if @config[:ProxyTimeout]
284
+ ################################## these issues are
285
+ http.open_timeout = 30 # secs # necessary (maybe bacause
286
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
287
+ ##################################
288
+ end
289
+ response = yield(http, path, header)
290
+ 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
+ end
304
+ end
305
+ end