apache-config-generator/lib/apache/rewrites.rb

404 lines
9.6 KiB
Ruby
Raw Normal View History

2010-05-11 13:25:29 +00:00
require 'pp'
2010-05-05 16:25:07 +00:00
module Apache
2010-05-10 19:57:34 +00:00
# Handle the creation of RewriteRules, RewriteConds, Redirects, and RedirectMatches
2010-05-05 16:25:07 +00:00
module Rewrites
2010-05-10 19:57:34 +00:00
# Enable the rewrite engine, optionally setting the logging level
#
# enable_rewrite_engine :log_level => 1 #=>
# RewriteEngine on
# RewriteLogLevel 1
2010-05-10 21:43:34 +00:00
def enable_rewrite_engine(options = {})
2010-05-19 13:37:57 +00:00
blank_line!
2010-05-05 16:25:07 +00:00
rewrite_engine! :on
options.each do |option, value|
case option
when :log_level
rewrite_log_level! value
end
end
2010-05-19 13:37:57 +00:00
blank_line!
2010-05-05 16:25:07 +00:00
end
2010-05-10 19:57:34 +00:00
# Pass the block to RewriteManager.build
2010-05-11 19:19:16 +00:00
def rewrites(*opt, &block)
self + indent(RewriteManager.build(*opt, &block))
2010-05-06 14:40:45 +00:00
self << ''
2010-05-05 16:25:07 +00:00
end
2010-05-11 19:19:16 +00:00
def rewrite(*opt, &block)
2010-05-18 17:08:17 +00:00
raise "You probably want rewrites #{opt.quoteize * " "} do" if block
2010-05-11 19:19:16 +00:00
end
2010-05-10 19:57:34 +00:00
# Create a permanent Redirect
#
# r301 '/here', '/there' #=> Redirect permanent "/here" "/there"
def r301(*opt)
2010-05-19 13:48:33 +00:00
if first = opt.first
if !first.kind_of?(::String)
raise "First parameter should be a String. Did you mean to wrap this in a rewrites block? #{first}"
end
2010-05-12 18:02:22 +00:00
end
2010-05-18 17:08:17 +00:00
self << "Redirect permanent #{opt.quoteize * " "}"
end
2010-05-05 16:25:07 +00:00
end
2010-05-10 19:57:34 +00:00
# Handle the creation of Rewritable things
2010-05-05 16:25:07 +00:00
class RewriteManager
class << self
attr_accessor :rewrites
2010-05-10 19:57:34 +00:00
# Reset the current list of rewrites
2010-05-07 20:04:06 +00:00
def reset!
2010-05-05 16:25:07 +00:00
@rewrites = []
2010-05-19 13:37:57 +00:00
@any_tests = false
@needs_tests = false
2010-05-07 20:04:06 +00:00
end
2010-05-10 19:57:34 +00:00
# Build rewritable things from the provided block
2010-05-11 19:19:16 +00:00
def build(*opt, &block)
2010-05-07 20:04:06 +00:00
reset!
2010-05-05 16:25:07 +00:00
self.instance_eval(&block)
2010-05-19 13:37:57 +00:00
name = block_name(opt.first)
2010-05-11 19:19:16 +00:00
2010-05-19 13:37:57 +00:00
show_messages! name
2010-05-11 19:19:16 +00:00
2010-05-19 13:37:57 +00:00
[ "# #{name}", @rewrites.collect(&:to_a) ].flatten
end
def block_name(first)
first_rewrite = @rewrites.first
first || (@rewrites.empty? ? 'unnamed block' : "#{first_rewrite.from} => #{first_rewrite.to}")
end
2010-05-11 19:19:16 +00:00
2010-05-19 13:37:57 +00:00
def show_messages!(name)
blue = "rewrite".foreground(:blue)
2010-05-11 19:19:16 +00:00
2010-05-19 13:37:57 +00:00
puts " [#{blue}] no tests found for #{name}" if !@any_tests && !@rewrites.empty?
puts " [#{blue}] #{name} needs more tests" if @needs_tests
2010-05-07 02:26:12 +00:00
end
2010-05-10 19:57:34 +00:00
# Commit the latest rewritable thing to the list of rewrites
2010-05-07 02:26:12 +00:00
def commit!
@rewrites << @rewrite
@rewrite = nil
end
2010-05-10 19:57:34 +00:00
# Ensure that there's a RewriteRule to be worked with
2010-05-07 02:26:12 +00:00
def ensure_rewrite!
@rewrite = RewriteRule.new if !@rewrite
2010-05-05 16:25:07 +00:00
end
2010-05-10 19:57:34 +00:00
# Create a RewriteRule with the given options
#
# rewrite %r{/here(.*)}, '/there$1', :last => true #=>
# RewriteRule "/here(.*)" "/there$1" [L]
2010-05-05 16:25:07 +00:00
def rewrite(*opts)
2010-05-07 02:26:12 +00:00
ensure_rewrite!
2010-05-05 16:25:07 +00:00
@rewrite.rule(*opts)
2010-05-07 02:26:12 +00:00
commit!
end
2010-05-05 16:25:07 +00:00
2010-05-07 20:04:06 +00:00
alias :rule :rewrite
2010-05-10 19:57:34 +00:00
# Create a RewriteCond with the given options
#
# cond "%{REQUEST_FILENAME}", "^/here" #=>
# RewriteCond "%{REQUEST_FILENAME}", "^/here"
2010-05-07 02:26:12 +00:00
def cond(*opts)
ensure_rewrite!
@rewrite.cond(*opts)
2010-05-05 16:25:07 +00:00
end
2010-05-10 19:57:34 +00:00
# Create a permanent RedirectMatch
#
# r301 %r{/here(.*)}, "/there$1" #=>
# RedirectMatch permanent "/here(.*)" "/there$1"
def r301(*opts)
redirect = RedirectMatchPermanent.new
redirect.rule(*opts)
@rewrites << redirect
end
2010-05-10 19:57:34 +00:00
# Test the rewritable things defined in this block
2010-05-05 16:25:07 +00:00
def rewrite_test(from, to, opts = {})
2010-05-11 19:19:16 +00:00
@any_tests = true
2010-05-05 16:25:07 +00:00
orig_from = from.dup
2010-05-19 13:37:57 +00:00
@rewrites.each do |rewrite|
if rewrite.match?(from, opts)
from = rewrite.from_tester(from, opts)
break if rewrite.stop_if_match?
2010-05-11 13:25:29 +00:00
end
2010-05-05 16:25:07 +00:00
end
if from != to
2010-05-19 13:37:57 +00:00
[ "#{orig_from} >> #{to} failed!", "Result: #{from}"
].each do |line|
puts " [#{"rewrite".foreground(:blue)}] #{line}"
end
2010-05-05 16:25:07 +00:00
end
end
2010-05-11 19:19:16 +00:00
def needs_tests
@needs_tests = true
end
def cond_not_a_file(opts = {})
cond_file_flag '!-f', opts
end
def cond_is_a_file(opts = {})
cond_file_flag '-f', opts
end
def cond_not_a_directory(opts = {})
cond_file_flag '!-d', opts
end
def cond_is_a_directory(opts = {})
cond_file_flag '-d', opts
end
private
def cond_file_flag(flag, opts)
cond opts[:filename_only] ? "%{REQUEST_FILENAME}" : "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}", flag
end
2010-05-05 16:25:07 +00:00
end
end
2010-05-10 19:57:34 +00:00
# Common methods for testing rewritable things that use regular expressions
2010-05-07 20:04:06 +00:00
module RegularExpressionMatcher
2010-05-10 19:57:34 +00:00
# Test this rewritable thing
2010-05-07 20:04:06 +00:00
def test(from, opts = {})
2010-05-18 21:43:23 +00:00
from.gsub(@from, @to.gsub(/\$([0-9])/) { |match| '\\' + $1 }).replace_placeholderize(opts)
end
2010-05-11 13:25:29 +00:00
def match?(from, opts = {})
2010-05-18 21:43:23 +00:00
from.replace_placeholderize(opts)[@from]
2010-05-07 20:04:06 +00:00
end
end
2010-05-10 19:57:34 +00:00
# A matchable thing to be extended
class MatchableThing
2010-05-11 19:19:16 +00:00
attr_reader :from, :to
2010-05-10 19:57:34 +00:00
# The Apache directive tag for this thing
def tag; raise 'Override this method'; end
2010-05-05 16:25:07 +00:00
def initialize
@from = nil
@to = nil
end
2010-05-07 20:04:06 +00:00
def rule(from, to)
2010-05-18 21:43:23 +00:00
raise "from must be a Regexp" if (!from.kind_of?(Regexp) && require_regexp?)
2010-05-05 16:25:07 +00:00
@from = from
@to = to
2010-05-07 02:26:12 +00:00
end
2010-05-07 20:04:06 +00:00
def to_s
2010-05-18 17:08:17 +00:00
"#{tag} #{[@from, @to].quoteize.compact.flatten * " "}"
2010-05-07 20:04:06 +00:00
end
2010-05-07 02:26:12 +00:00
2010-05-07 20:04:06 +00:00
def to_a
[ to_s ]
2010-05-05 16:25:07 +00:00
end
2010-05-11 13:25:29 +00:00
def stop_if_match?; false; end
2010-05-11 19:19:16 +00:00
def forbidden?; false; end
2010-05-18 21:43:23 +00:00
def require_regexp?; false; end
2010-05-19 13:37:57 +00:00
def from_tester(from, opts)
from = test(from, opts)
from = @from if (@to == '-')
from = :http_forbidden if (forbidden?)
from
end
2010-05-07 20:04:06 +00:00
end
2010-05-05 16:25:07 +00:00
2010-05-10 19:57:34 +00:00
# A RewriteRule definition
2010-05-07 20:04:06 +00:00
class RewriteRule < MatchableThing
include RegularExpressionMatcher
def tag; 'RewriteRule'; end
def initialize
super
@conditions = []
@options = nil
2010-05-11 13:25:29 +00:00
@input_options = {}
2010-05-05 16:25:07 +00:00
end
2010-05-10 19:57:34 +00:00
# Define the rule, passing in additional options
#
# rule %r{^/here}, '/there', { :last => true, :preserve_query_string => true }
2010-05-18 21:43:23 +00:00
#
# Options for the options hash are:
# * :last => true #=> [L]
# * :forbidden => true #=> [F]
# * :no_escape => true #=> [NE]
# * :redirect => true #=> [R]
# * :redirect => 302 #=> [R=302]
# * :pass_through => true #=> [PT]
# * :preserve_query_string => true #=> [QSA]
# * :query_string_append => true #=> [QSA]
# * :env => 'what' #=> [E=what]
2010-05-11 13:25:29 +00:00
def rule(from, to, options = {})
2010-05-07 20:04:06 +00:00
super(from, to)
2010-05-11 13:25:29 +00:00
@input_options = options
2010-05-18 21:43:23 +00:00
@options = options.rewrite_rule_optionify.rewrite_option_listify
2010-05-07 20:04:06 +00:00
end
2010-05-05 16:25:07 +00:00
2010-05-10 19:57:34 +00:00
# Add a RewriteCondition to this RewriteRule
2010-05-07 20:04:06 +00:00
def cond(from, to, *opts)
rewrite_cond = RewriteCondition.new
rewrite_cond.cond(from, to, *opts)
2010-05-05 16:25:07 +00:00
2010-05-07 20:04:06 +00:00
@conditions << rewrite_cond
2010-05-05 16:25:07 +00:00
end
2010-05-19 14:11:20 +00:00
def initial_blank!
2010-05-19 14:34:49 +00:00
@conditions.empty? ? nil : ''
2010-05-19 14:11:20 +00:00
end
2010-05-07 20:04:06 +00:00
def to_s
2010-05-18 17:08:17 +00:00
"#{tag} #{[@from.source.quoteize, @to.quoteize, @options].compact.flatten * " "}"
2010-05-07 20:04:06 +00:00
end
def to_a
2010-05-19 14:34:49 +00:00
[ initial_blank!, @conditions.collect(&:to_s), super ].flatten
2010-05-07 20:04:06 +00:00
end
2010-05-10 19:57:34 +00:00
# Test this RewriteRule, ensuring the RewriteConds also match
def test(from, opts = {})
2010-05-19 14:34:49 +00:00
ensure_opts!(opts)
2010-05-11 19:19:16 +00:00
opts[:request_uri] = from
2010-05-11 13:25:29 +00:00
result = from
result = super(from, opts) if match?(from, opts)
2010-05-18 21:43:23 +00:00
result.replace_placeholderize(opts)
2010-05-11 13:25:29 +00:00
end
2010-05-19 14:34:49 +00:00
def ensure_opts!(opts)
raise "Options must be a hash" if !opts
raise "Options must be a hash" if !opts.kind_of? ::Hash
end
2010-05-11 13:25:29 +00:00
def match?(from, opts = {})
2010-05-19 14:34:49 +00:00
ensure_opts!(opts)
2010-05-11 19:19:16 +00:00
opts[:request_uri] = from
2010-05-11 13:25:29 +00:00
2010-05-18 21:43:23 +00:00
@conditions.each do |cond|
return false if !cond.test(from, opts)
end
2010-05-18 21:43:23 +00:00
super(from, opts)
end
2010-05-11 13:25:29 +00:00
def stop_if_match?
@input_options[:last]
end
2010-05-11 19:19:16 +00:00
def forbidden?
@input_options[:forbidden]
end
2010-05-18 21:43:23 +00:00
def require_regexp?; true; end
end
2010-05-10 19:57:34 +00:00
# A permanent RedirectMatch
class RedirectMatchPermanent < MatchableThing
2010-05-07 20:04:06 +00:00
include RegularExpressionMatcher
2010-05-18 21:43:23 +00:00
# The Apache directive for this object.
def tag; 'RedirectMatch permanent'; end
2010-05-07 20:04:06 +00:00
2010-05-18 21:43:23 +00:00
# Define a RedirectMatch rule.
2010-05-10 19:57:34 +00:00
def rule(from, to)
super(from, to)
raise "from must be a Regexp" if !from.kind_of?(Regexp)
end
2010-05-18 21:43:23 +00:00
# Convert this tag to a String.
2010-05-07 20:04:06 +00:00
def to_s
2010-05-18 17:08:17 +00:00
"#{tag} #{[@from.source, @to].quoteize.compact.flatten * " "}"
2010-05-07 20:04:06 +00:00
end
2010-05-11 13:25:29 +00:00
2010-05-18 21:43:23 +00:00
# Stop rewrite testing if this object matches.
2010-05-11 13:25:29 +00:00
def stop_if_match; true; end
2010-05-18 21:43:23 +00:00
def require_regexp?; true; end
2010-05-05 16:25:07 +00:00
end
2010-05-07 02:26:12 +00:00
2010-05-10 19:57:34 +00:00
# A RewriteCond
2010-05-07 02:26:12 +00:00
class RewriteCondition < MatchableThing
include RegularExpressionMatcher
2010-05-07 02:26:12 +00:00
def tag; 'RewriteCond'; end
2010-05-07 20:04:06 +00:00
2010-05-10 19:57:34 +00:00
# Define a RewriteCond
#
# rule "%{REQUEST_FILENAME}", "^/here", :case_insensitive #=>
# RewriteCond "%{REQUEST_FILENAME}" "^/here" [NC]
2010-05-18 21:43:23 +00:00
#
# Additional parameters can include the following:
# * :or #=> [OR]
# * :case_insensitive #=> [NC]
# * :no_vary #=> [NV]
2010-05-07 20:04:06 +00:00
def rule(from, to, *opts)
super(from, to)
2010-05-18 21:43:23 +00:00
@options = opts.rewrite_cond_optionify.rewrite_option_listify
2010-05-07 20:04:06 +00:00
end
2010-05-07 02:26:12 +00:00
alias :cond :rule
2010-05-18 21:43:23 +00:00
# Create a new RewriteCond
2010-05-07 20:04:06 +00:00
def initialize
super
@options = nil
end
2010-05-18 21:43:23 +00:00
# Convert this tag to a String.
2010-05-07 02:26:12 +00:00
def to_s
2010-05-18 17:08:17 +00:00
"#{tag} #{[@from.quoteize, @to.quoteize, @options].compact.flatten * " "}"
2010-05-07 02:26:12 +00:00
end
2010-05-19 13:37:57 +00:00
def inverse_result?
@to[0..0] == '!'
end
def actual_to
to = @to
to = to[1..-1] if inverse_result?
to
end
2010-05-10 19:57:34 +00:00
# Test this RewriteCond
def test(from, opts = {})
super(from, opts)
2010-05-18 21:43:23 +00:00
source = @from.replace_placeholderize(opts)
2010-05-19 13:37:57 +00:00
to = actual_to
2010-05-11 19:19:16 +00:00
2010-05-12 18:24:35 +00:00
case to
when '-f'
result = opts[:files].include?(source) if opts[:files]
else
2010-05-11 19:19:16 +00:00
result = source[Regexp.new(to)]
end
2010-05-19 13:37:57 +00:00
inverse_result? ? !result : result
end
2010-05-07 02:26:12 +00:00
end
2010-05-05 16:25:07 +00:00
end