2009/09/02
コンテンツフィルターをwebrickで頑張ってみる
RAA - reverse_proxyがあったので、それを使ってやってみる。
#!/usr/bin/env ruby
require 'webrick'
require 'net/http'
module WEBrick
class WEBrick::HTTPReverseProxyServer < WEBrick::HTTPServer
def service(req, res)
rule = first_matching_proxy_rule(req)
if rule.nil?
super(req, res)
else
service_proxy(req, res, rule)
end
end
# find the *first* matching pattern in the proxy map
def first_matching_proxy_rule(req)
matching_rule = @config[:ProxyRules].detect { |rule|
re = Regexp.new(rule.pattern)
m = re.match(req.request_uri.to_s)
not m.nil?
}
return matching_rule
end
def map_to_proxyURI(req, rule)
path = (req.path).sub(%r!#{rule.pattern}!, rule.replacement)
path += '?' + req.query_string if req.query_string
return [rule.host, rule.port, path]
end
def service_proxy(req, res, rule)
host, port, path = map_to_proxyURI(req, rule)
# convert WEBrick header (values wrapped in an array) into Net::HTTP header (simple values)
header = {}
req.header.keys { |key| header[key] = req.header[key][0] }
header['x-forwarded-for'] = req.peeraddr[2] # the name of the requesting host
header['host'] = host
# send the new request to the private server (hacked from WEBrick::HTTPProxyServer)
response = nil
begin
http = Net::HTTP.new(host, port)
if port.to_i == 443
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
http.start {
case req.request_method
when "GET" then response = http.get(path, header)
when "POST" then response = http.post(path, req.body || "", header)
when "HEAD" then response = http.head(path, header)
else
raise HTTPStatus::MethodNotAllowed, "unsupported method `#{req.request_method}'."
end
}
rescue => err
logger.debug("#{err.class}: #{err.message}")
raise HTTPStatus::ServiceUnavailable, err.message
end
res['connection'] = "close"
# Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse
res.status = response.code.to_i
response.each { |key, val| res[key] = val }
res.body = response.body
@config[:FilterRules].each{|rule|
regex = Regexp.new(rule.pattern)
unless res.header['location'].nil?
res.header['location'].gsub!(regex, rule.replacement)
end
res.body.gsub!(regex, rule.replacement);
}
@config[:CookieFilterRules].each{|rule|
regex = Regexp.new(rule.pattern)
unless res['set-cookie'].nil?
res['set-cookie'].gsub!(regex, rule.replacement)
end
}
res.header['content-length'] = res.body.size
puts res.header['content-length']
# Process contents
if handler = @config[:ProxyContentHandler]
handler.call(req, res)
end
end
end
ProxyRule = Struct.new("ProxyRule", :pattern, :host, :port, :replacement)
FilterRule = Struct.new("FilterRule", :pattern, :replacement)
end
server = WEBrick::HTTPReverseProxyServer.new(
:ServerName => 'internal.domain',
:BindAddress => '0.0.0.0',
:Port => 10080,
:Logger => WEBrick::Log.new($stderr, WEBrick::Log::DEBUG),
:ProxyVia => false,
:ProxyRules => [
WEBrick::ProxyRule.new('http://internal.domain', 'external.domain', '80', '/'),
WEBrick::ProxyRule.new('https://secure.internal.domain', 'secure.external.domain', '443', '/'),
],
:FilterRules => [
WEBrick::FilterRule.new('https://secure.external.domain', 'https://secure.internal.domain:10083'),
WEBrick::FilterRule.new('http://img.external.domain', 'http://img.internal.domain:10080'),
WEBrick::FilterRule.new('http://external.domain', 'http://internal.domain:10080'),
],
:CookieFilterRules => [
WEBrick::FilterRule.new('.external.domain', '.internal.domain'),
]
)
[:INT, :TERM].each {|signal|
Signal.trap(signal) {
server.shutdown
}
}
server.start
Apache(httpd) の mod_proxy_html + mod_ext_filter + mod_proxy (ProxyPassReverse, ProxyPassReverseCookieDomain) とか使って、コンテンツの書き換えとかcookieまわりを頑張って書き換えてみたけど、webrick でも結構頑張るなぁ...(しかも不完全だし)
うーん。難しい。
Trackback
No Trackbacks
Track from Your Website
http://blog.xole.net/trackback/tb.php?id=730

Comment
No Comments