shuber-proxy 1.3.0 → 1.3.1
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.
- data/CHANGELOG +6 -0
- data/README.markdown +48 -12
- data/Rakefile +10 -8
- data/lib/proxy.rb +31 -10
- data/lib/proxy/action_controller/abstract_request.rb +41 -0
- data/lib/proxy/action_controller/base.rb +120 -0
- data/lib/proxy/action_controller/named_route_collection.rb +30 -0
- data/lib/proxy/action_controller/url_rewriter.rb +21 -0
- data/lib/proxy/action_view/url_helper.rb +21 -0
- data/test/proxy_test.rb +34 -0
- metadata +10 -9
- data/lib/huberry/proxy/action_controller/abstract_request.rb +0 -43
- data/lib/huberry/proxy/action_controller/base.rb +0 -122
- data/lib/huberry/proxy/action_controller/named_route_collection.rb +0 -32
- data/lib/huberry/proxy/action_controller/url_rewriter.rb +0 -23
- data/lib/huberry/proxy/action_view/url_helper.rb +0 -23
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
2009-06-04 - Sean Huber (shuber@huberry.com)
|
2
|
+
* Update test related rake tasks
|
3
|
+
* Remove huberry namespace
|
4
|
+
* Add support for multiple domains
|
5
|
+
* Update README
|
6
|
+
|
1
7
|
2009-05-20 - Sean Huber (shuber@huberry.com)
|
2
8
|
* Update date in gemspec so github rebuilds gem
|
3
9
|
|
data/README.markdown
CHANGED
@@ -1,25 +1,24 @@
|
|
1
|
-
Proxy
|
2
|
-
=====
|
1
|
+
# Proxy
|
3
2
|
|
4
|
-
A gem/plugin that allows rails applications to dynamically respond to proxied requests by detecting forwarded host/uri headers and setting the session domain, default host, and relative url root. The plugin adds this functionality to calls to url\_for, named route helpers, and view url helpers while still allowing you to specifically set the :host and :only\_path options to override this behavior.
|
3
|
+
A gem/plugin that allows rails applications to dynamically respond to multiple domains and proxied requests by detecting forwarded host/uri headers and setting the session domain, default host, and relative url root. The plugin adds this functionality to calls to url\_for, named route helpers, and view url helpers while still allowing you to specifically set the :host and :only\_path options to override this behavior.
|
5
4
|
|
6
5
|
The original session domain, default host, and relative url root will be restored after each request.
|
7
6
|
|
8
7
|
Requires actionpack version >= 2.0.0
|
9
8
|
|
10
9
|
|
11
|
-
Installation
|
12
|
-
------------
|
10
|
+
## Installation
|
13
11
|
|
14
12
|
script/plugin install git://github.com/shuber/proxy.git
|
15
13
|
OR
|
16
14
|
gem install shuber-proxy --source http://gems.github.com
|
17
15
|
|
18
16
|
|
19
|
-
Usage
|
20
|
-
-----
|
17
|
+
## Usage
|
21
18
|
|
22
|
-
|
19
|
+
### Proxied Requests
|
20
|
+
|
21
|
+
Let's say you have a suite of hosted applications all running on the same domain but mounted on different paths. One of them is an order/invoicing application located at:
|
23
22
|
|
24
23
|
http://client.example.com/orders
|
25
24
|
|
@@ -29,6 +28,9 @@ Imagine you sold an account to a client but the client wants the application to
|
|
29
28
|
|
30
29
|
This plugin will automatically detect this forwarded host and set the session domain and default host (for url generation) accordingly.
|
31
30
|
|
31
|
+
|
32
|
+
### Proxied Requests with Custom URIs
|
33
|
+
|
32
34
|
Now imagine the client had an existing ordering system already running at /orders, and he wants to slowly migrate his data into your application, so he'll need both applications running for awhile. He wants to keep his original ordering application running at /orders and he wants your application running at:
|
33
35
|
|
34
36
|
http://clientdomain.com/neworders
|
@@ -42,8 +44,7 @@ Note: this plugin looks for a request header called 'HTTP\_X\_FORWARDED\_URI' to
|
|
42
44
|
You can add that line in environment.rb or an initializer.
|
43
45
|
|
44
46
|
|
45
|
-
Relative Url Root Proxy Setup
|
46
|
-
-----------------------------
|
47
|
+
#### Relative Url Root Proxy Setup
|
47
48
|
|
48
49
|
The client's proxy must forward the request uri header in order for this plugin to automatically set the relative url root correctly. Here is how the client would setup a proxy in apache for the example above:
|
49
50
|
|
@@ -51,7 +52,42 @@ The client's proxy must forward the request uri header in order for this plugin
|
|
51
52
|
RequestHeader append X_FORWARDED_URI %{originalUri}e e=originalUri
|
52
53
|
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
### Multiple Domains
|
56
|
+
|
57
|
+
Imagine you have a CMS that hosts multiple client sites. You want your users to manage their sites on your root domain `http://yourcmsapp.com` and you display a site's public content when it's accessed by its subdomain (e.g. `http://cool-site.yourcmsapp.com`). You'll probably be using [subdomain-fu](http://github.com/mbleigh/subdomain-fu) so you can route based on subdomains like:
|
58
|
+
|
59
|
+
ActionController::Routing::Routes.draw do |map|
|
60
|
+
# this routing controller has a before_filter callback that looks up a site by subdomain
|
61
|
+
map.public_page '*path', :controller => 'routing', :conditions => { :subdomain => /^[^\.]+$/ }
|
62
|
+
|
63
|
+
map.with_options :conditions => { :subdomain => nil } do |admin|
|
64
|
+
admin.resource :account, :controller => 'account'
|
65
|
+
admin.resources :sites
|
66
|
+
...
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Now, it gets tricky if you want `http://cool-site.com` to render `cool-site`'s public content because you can't tell if this request has a subdomain or not. In order for your routes to work, you must have all requests coming in from your domain `yourcmsapp.com`. You can accomplish this by calling the `Proxy.replace_host_with(&block)` method like so:
|
71
|
+
|
72
|
+
class ApplicationController < ActionController::Base
|
73
|
+
|
74
|
+
# you can put this in an initializer or something instead if you'd like
|
75
|
+
Proxy.replace_host_with do |request|
|
76
|
+
"#{Site.find_by_domain(request.host).try(:subdomain) || '-INVALID-'}.yourcmsapp.com" unless request.host =~ /(.*\.|^)yourcmsapp.com$/i
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
Let's examine what this block is doing:
|
82
|
+
|
83
|
+
* First, it checks if the current request's host is already on your domain. If it is, we don't need to do anything, otherwise...
|
84
|
+
* It checks if a site exists with a domain that matches the current request's host.
|
85
|
+
* If a site does exist, a new host is returned using the site's `subdomain` with your app domain and everything renders fine, otherwise...
|
86
|
+
* A fake host is returned (-INVALID-.yourcmsapp.com), and the request 404s once it gets to your `routing` controller and a site can't be found with the subdomain `-INVALID-`
|
87
|
+
|
88
|
+
If `nil, false, or an empty string` is returned when you call the `Proxy.replace_host_with` method, the current request's host is not modified. Otherwise, the `HTTP_X_FORWARDED_HOST` request header is set to: `"#{the_original_host}, #{the_new_host}"`. This allows your routes to use your domain when evaluating routing conditions and also allows all of the application's url generators to use the original host.
|
89
|
+
|
90
|
+
|
91
|
+
## Contact
|
56
92
|
|
57
93
|
Problems, comments, and suggestions all welcome: [shuber@huberry.com](mailto:shuber@huberry.com)
|
data/Rakefile
CHANGED
@@ -3,15 +3,17 @@ require 'rake/testtask'
|
|
3
3
|
require 'rake/rdoctask'
|
4
4
|
|
5
5
|
desc 'Default: run the proxy tests'
|
6
|
-
task :default => :
|
6
|
+
task :default => :test
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
namespace :test do
|
9
|
+
desc 'Default: run the proxy tests for all versions of actionpack'
|
10
|
+
task :all do
|
11
|
+
versions = `gem list`.match(/actionpack \((.+)\)/).captures[0].split(/, /).select { |v| v[0,1].to_i > 1 }
|
12
|
+
versions.each do |version|
|
13
|
+
puts "\n\n============================================================="
|
14
|
+
puts "TESTING WITH ACTION PACK VERSION #{version}\n\n"
|
15
|
+
system "rake test ACTION_PACK_VERSION=#{version}"
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
data/lib/proxy.rb
CHANGED
@@ -1,15 +1,36 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
1
|
+
require 'proxy/action_controller/abstract_request'
|
2
|
+
require 'proxy/action_controller/base'
|
3
|
+
require 'proxy/action_controller/named_route_collection'
|
4
|
+
require 'proxy/action_controller/url_rewriter'
|
5
|
+
require 'proxy/action_view/url_helper'
|
6
|
+
|
7
|
+
module Proxy
|
8
|
+
mattr_accessor :replace_host_with_proc
|
9
|
+
self.replace_host_with_proc = proc { |request| }
|
10
|
+
|
11
|
+
def self.replace_host_with(&block)
|
12
|
+
self.replace_host_with_proc = block
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.before_dispatch(dispatcher)
|
18
|
+
request = dispatcher.instance_variable_get('@request')
|
19
|
+
new_host = replace_host_with_proc.call(request)
|
20
|
+
request.env['HTTP_X_FORWARDED_HOST'] = [request.host, new_host].join(', ') unless new_host.blank?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActionController::Dispatcher.before_dispatch do |dispatcher|
|
25
|
+
Proxy.send :before_dispatch, dispatcher
|
26
|
+
end
|
6
27
|
|
7
28
|
ActionController::AbstractRequest = ActionController::Request if defined?(ActionController::Request)
|
8
|
-
ActionController::AbstractRequest.send :include,
|
9
|
-
ActionController::Base.send :include,
|
10
|
-
ActionController::Routing::RouteSet::NamedRouteCollection.send :include,
|
11
|
-
ActionController::UrlRewriter.send :include,
|
12
|
-
ActionView::Base.send :include,
|
29
|
+
ActionController::AbstractRequest.send :include, Proxy::ActionController::AbstractRequest
|
30
|
+
ActionController::Base.send :include, Proxy::ActionController::Base
|
31
|
+
ActionController::Routing::RouteSet::NamedRouteCollection.send :include, Proxy::ActionController::NamedRouteCollection
|
32
|
+
ActionController::UrlRewriter.send :include, Proxy::ActionController::UrlRewriter
|
33
|
+
ActionView::Base.send :include, Proxy::ActionView::UrlHelper
|
13
34
|
|
14
35
|
unless ActionController::UrlWriter.respond_to?(:default_url_options)
|
15
36
|
ActionController::Base.class_eval do
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Proxy
|
2
|
+
module ActionController
|
3
|
+
module AbstractRequest
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
mattr_accessor :forwarded_uri_header_name
|
7
|
+
self.forwarded_uri_header_name = 'HTTP_X_FORWARDED_URI'
|
8
|
+
memoize :forwarded_hosts, :forwarded_uris if respond_to? :memoize
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Parses the forwarded host header and returns an array of forwarded hosts
|
13
|
+
#
|
14
|
+
# For example:
|
15
|
+
#
|
16
|
+
# If the HTTP_X_FORWARDED_HOST header was set to
|
17
|
+
# 'some-domain.com, some-other-domain.com, and-another-domain.com'
|
18
|
+
#
|
19
|
+
# This method would return ['some-domain.com', 'some-other-domain.com', 'and-another-domain.com']
|
20
|
+
#
|
21
|
+
# Returns an empty array if there aren't any forwarded hosts
|
22
|
+
def forwarded_hosts
|
23
|
+
env['HTTP_X_FORWARDED_HOST'].to_s.split(/,\s*/)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Parses the forwarded uri header and returns an array of forwarded uris
|
27
|
+
#
|
28
|
+
# For example:
|
29
|
+
#
|
30
|
+
# If the HTTP_X_FORWARDED_URI header was set to
|
31
|
+
# '/some/path, /some/other/path, /and/another/path'
|
32
|
+
#
|
33
|
+
# This method would return ['/some/path, '/some/other/path', '/and/another/path']
|
34
|
+
#
|
35
|
+
# Returns an empty array if there aren't any forwarded uris
|
36
|
+
def forwarded_uris
|
37
|
+
env[self.forwarded_uri_header_name].to_s.split(/,\s*/)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Proxy
|
2
|
+
module ActionController
|
3
|
+
module Base
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
before_filter :set_proxy_relative_url_root
|
7
|
+
around_filter :swap_default_host
|
8
|
+
around_filter :swap_relative_url_root
|
9
|
+
around_filter :swap_session_domain
|
10
|
+
cattr_accessor :original_relative_url_root
|
11
|
+
cattr_accessor :proxy_relative_url_root
|
12
|
+
alias_method_chain :redirect_to, :proxy
|
13
|
+
class << self; delegate :relative_url_root, :relative_url_root=, :to => ::ActionController::AbstractRequest unless ::ActionController::Base.respond_to?(:relative_url_root); end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
# Calculates the <tt>relative_url_root</tt> by parsing the request path out of the
|
20
|
+
# first forwarded uri
|
21
|
+
#
|
22
|
+
# For example:
|
23
|
+
#
|
24
|
+
# http://example.com/manage/videos/new
|
25
|
+
# gets proxied to
|
26
|
+
# http://your-domain.com/videos/new
|
27
|
+
#
|
28
|
+
# The first forwarded uri would be: /manage/videos/new
|
29
|
+
# and the request path would be: /videos/new
|
30
|
+
#
|
31
|
+
# So this method would return: /manage
|
32
|
+
def parse_proxy_relative_url_root
|
33
|
+
request.forwarded_uris.first.gsub(/#{Regexp.escape(request.path)}$/, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calculates the <tt>session_domain</tt> by parsing the first domain.tld out of the
|
37
|
+
# first forwarded host and prepending a '.'
|
38
|
+
#
|
39
|
+
# For example:
|
40
|
+
#
|
41
|
+
# http://example.com/manage/videos/new
|
42
|
+
# http://some.other-domain.com/videos/new
|
43
|
+
# both get proxied to
|
44
|
+
# http://your-domain.com/videos/new
|
45
|
+
#
|
46
|
+
# The resulting session domain for the first url would be: '.example.com'
|
47
|
+
# The resulting session domain for the second url would be: '.other-domain.com'
|
48
|
+
def parse_session_domain
|
49
|
+
".#{$1}" if /([^\.]+\.[^\.]+)$/.match(request.forwarded_hosts.first)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Forces redirects to use the <tt>default_url_options[:host]</tt> if it exists unless a host
|
53
|
+
# is already set
|
54
|
+
#
|
55
|
+
# For example:
|
56
|
+
#
|
57
|
+
# http://example.com
|
58
|
+
# gets proxied to
|
59
|
+
# http://your-domain.com
|
60
|
+
#
|
61
|
+
# If you have an action that calls <tt>redirect_to new_videos_path</tt>, the example.com domain
|
62
|
+
# would be used instead of your-domain.com
|
63
|
+
def redirect_to_with_proxy(*args)
|
64
|
+
args[0] = request.protocol + ::ActionController::UrlWriter.default_url_options[:host] + args.first if args.first.is_a?(String) && !%r{^\w+://.*}.match(args.first) && !::ActionController::UrlWriter.default_url_options[:host].blank?
|
65
|
+
redirect_to_without_proxy(*args)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the <tt>proxy_relative_url_root</tt> using the +parse_proxy_relative_url_root+ method
|
69
|
+
# to calculate it
|
70
|
+
#
|
71
|
+
# Sets the <tt>proxy_relative_url_root</tt> to nil if there aren't any forwarded uris
|
72
|
+
def set_proxy_relative_url_root
|
73
|
+
::ActionController::Base.proxy_relative_url_root = request.forwarded_uris.empty? ? nil : parse_proxy_relative_url_root
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets the <tt>default_url_options[:host]</tt> to the first forwarded host if there are any
|
77
|
+
#
|
78
|
+
# The original default host is restored after each request and can be accessed by calling
|
79
|
+
# <tt>ActionController::UrlWriter.default_url_options[:original_host]</tt>
|
80
|
+
def swap_default_host
|
81
|
+
::ActionController::UrlWriter.default_url_options[:original_host] = ::ActionController::UrlWriter.default_url_options[:host]
|
82
|
+
::ActionController::UrlWriter.default_url_options[:host] = request.forwarded_hosts.first unless request.forwarded_hosts.empty?
|
83
|
+
begin
|
84
|
+
yield
|
85
|
+
ensure
|
86
|
+
::ActionController::UrlWriter.default_url_options[:host] = ::ActionController::UrlWriter.default_url_options[:original_host]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sets the <tt>relative_url_root</tt> to the <tt>proxy_relative_url_root</tt> unless it's nil
|
91
|
+
#
|
92
|
+
# The original relative url root is restored after each request and can be accessed by calling
|
93
|
+
# <tt>ActionController::Base.original_relative_url_root</tt>
|
94
|
+
def swap_relative_url_root
|
95
|
+
::ActionController::Base.original_relative_url_root = ::ActionController::Base.relative_url_root
|
96
|
+
::ActionController::Base.relative_url_root = ::ActionController::Base.proxy_relative_url_root unless ::ActionController::Base.proxy_relative_url_root.nil?
|
97
|
+
begin
|
98
|
+
yield
|
99
|
+
ensure
|
100
|
+
::ActionController::Base.relative_url_root = ::ActionController::Base.original_relative_url_root
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Sets the <tt>session_options[:session_domain]</tt> to the result of the +parse_session_domain+ method
|
105
|
+
# unless there aren't any forwarded hosts
|
106
|
+
#
|
107
|
+
# The original session domain is restored after each request and can be accessed by calling
|
108
|
+
# <tt>ActionController::Base.session_options[:original_session_domain]</tt>
|
109
|
+
def swap_session_domain
|
110
|
+
::ActionController::Base.session_options[:original_session_domain] = ::ActionController::Base.session_options[:session_domain]
|
111
|
+
::ActionController::Base.session_options[:session_domain] = parse_session_domain unless request.forwarded_hosts.empty?
|
112
|
+
begin
|
113
|
+
yield
|
114
|
+
ensure
|
115
|
+
::ActionController::Base.session_options[:session_domain] = ::ActionController::Base.session_options[:original_session_domain]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Proxy
|
2
|
+
module ActionController
|
3
|
+
module NamedRouteCollection
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval { alias_method_chain :define_url_helper, :proxy }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Named route url helpers (not path helpers) don't seem to work correctly
|
9
|
+
# with forwarded hosts unless we explicitly set the option:
|
10
|
+
#
|
11
|
+
# :only_path => false
|
12
|
+
#
|
13
|
+
# This method only sets that option if it isn't set already
|
14
|
+
def define_url_helper_with_proxy(route, name, kind, options)
|
15
|
+
define_url_helper_without_proxy(route, name, kind, options)
|
16
|
+
if kind == :url
|
17
|
+
selector = url_helper_name(name, kind)
|
18
|
+
@module.module_eval do
|
19
|
+
define_method "#{selector}_with_proxy" do |*args|
|
20
|
+
args << {} unless args.last.is_a? Hash
|
21
|
+
args.last[:only_path] ||= false
|
22
|
+
send "#{selector}_without_proxy", *args
|
23
|
+
end
|
24
|
+
alias_method_chain selector, :proxy
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Proxy
|
2
|
+
module ActionController
|
3
|
+
module UrlRewriter
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval { alias_method_chain :rewrite_url, :proxy }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Adds the default :host option unless already specified
|
9
|
+
#
|
10
|
+
# It will not set the :host option if <tt>options</tt> is not a hash or
|
11
|
+
# if the <tt>ActionController::UrlWriter.default_url_options[:host]</tt> is blank
|
12
|
+
def rewrite_url_with_proxy(options)
|
13
|
+
if options.is_a?(Hash)
|
14
|
+
options[:host] ||= ::ActionController::UrlWriter.default_url_options[:host] unless ::ActionController::UrlWriter.default_url_options[:host].blank?
|
15
|
+
options.delete(:original_host)
|
16
|
+
end
|
17
|
+
rewrite_url_without_proxy(options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Proxy
|
2
|
+
module ActionView
|
3
|
+
module UrlHelper
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval { alias_method_chain :url_for, :proxy }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Adds the default :host option unless already specified
|
9
|
+
#
|
10
|
+
# It will not set the :host option if <tt>options</tt> is not a hash or
|
11
|
+
# if the <tt>ActionController::UrlWriter.default_url_options[:host]</tt> is blank
|
12
|
+
def url_for_with_proxy(options = {})
|
13
|
+
if options.is_a?(Hash)
|
14
|
+
options[:host] ||= ::ActionController::UrlWriter.default_url_options[:host] unless ::ActionController::UrlWriter.default_url_options[:host].blank?
|
15
|
+
options.delete(:original_host)
|
16
|
+
end
|
17
|
+
url_for_without_proxy(options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/test/proxy_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/init'
|
2
|
+
|
3
|
+
class ProxyTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@controller = TestController.new
|
7
|
+
@request = ActionController::TestRequest.new
|
8
|
+
def @request.host; env['HTTP_X_FORWARDED_HOST'].blank? ? env['HTTP_HOST'] : env['HTTP_X_FORWARDED_HOST'].split(/,\s/).last; end
|
9
|
+
@response = ActionController::TestResponse.new
|
10
|
+
@dispatcher = ActionController::Dispatcher.new(StringIO.new)
|
11
|
+
@dispatcher.instance_variable_set('@request', @request)
|
12
|
+
ActionController::UrlWriter.default_url_options[:host] = nil
|
13
|
+
ActionController::Base.relative_url_root = nil
|
14
|
+
|
15
|
+
Proxy.replace_host_with do |request|
|
16
|
+
'replaced.com' if request.host == 'replace-me.com'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_not_replace_host
|
21
|
+
@request.env['HTTP_HOST'] = 'dont-replace-me.com'
|
22
|
+
assert_equal 'dont-replace-me.com', @request.host
|
23
|
+
Proxy.send(:before_dispatch, @dispatcher)
|
24
|
+
assert_equal 'dont-replace-me.com', @request.host
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_should_replace_host
|
28
|
+
@request.env['HTTP_HOST'] = 'replace-me.com'
|
29
|
+
assert_equal 'replace-me.com', @request.host
|
30
|
+
Proxy.send(:before_dispatch, @dispatcher)
|
31
|
+
assert_equal 'replaced.com', @request.host
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shuber-proxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Huber
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-06-04 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description: A gem/plugin that allows rails applications to
|
16
|
+
description: A gem/plugin that allows rails applications to respond to multiple domains and proxied requests
|
17
17
|
email: shuber@huberry.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -24,11 +24,11 @@ extra_rdoc_files: []
|
|
24
24
|
files:
|
25
25
|
- CHANGELOG
|
26
26
|
- init.rb
|
27
|
-
- lib/
|
28
|
-
- lib/
|
29
|
-
- lib/
|
30
|
-
- lib/
|
31
|
-
- lib/
|
27
|
+
- lib/proxy/action_controller/abstract_request.rb
|
28
|
+
- lib/proxy/action_controller/base.rb
|
29
|
+
- lib/proxy/action_controller/named_route_collection.rb
|
30
|
+
- lib/proxy/action_controller/url_rewriter.rb
|
31
|
+
- lib/proxy/action_view/url_helper.rb
|
32
32
|
- lib/proxy.rb
|
33
33
|
- MIT-LICENSE
|
34
34
|
- Rakefile
|
@@ -61,10 +61,11 @@ rubyforge_project:
|
|
61
61
|
rubygems_version: 1.2.0
|
62
62
|
signing_key:
|
63
63
|
specification_version: 2
|
64
|
-
summary: A gem/plugin that allows rails applications to
|
64
|
+
summary: A gem/plugin that allows rails applications to respond to multiple domains and proxied requests
|
65
65
|
test_files:
|
66
66
|
- test/abstract_request_test.rb
|
67
67
|
- test/base_test.rb
|
68
68
|
- test/named_route_collection_test.rb
|
69
|
+
- test/proxy_test.rb
|
69
70
|
- test/url_helper_test.rb
|
70
71
|
- test/url_rewriter_test.rb
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Proxy
|
3
|
-
module ActionController
|
4
|
-
module AbstractRequest
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
mattr_accessor :forwarded_uri_header_name
|
8
|
-
self.forwarded_uri_header_name = 'HTTP_X_FORWARDED_URI'
|
9
|
-
memoize :forwarded_hosts, :forwarded_uris if respond_to? :memoize
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# Parses the forwarded host header and returns an array of forwarded hosts
|
14
|
-
#
|
15
|
-
# For example:
|
16
|
-
#
|
17
|
-
# If the HTTP_X_FORWARDED_HOST header was set to
|
18
|
-
# 'some-domain.com, some-other-domain.com, and-another-domain.com'
|
19
|
-
#
|
20
|
-
# This method would return ['some-domain.com', 'some-other-domain.com', 'and-another-domain.com']
|
21
|
-
#
|
22
|
-
# Returns an empty array if there aren't any forwarded hosts
|
23
|
-
def forwarded_hosts
|
24
|
-
env['HTTP_X_FORWARDED_HOST'].to_s.split(/,\s*/)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Parses the forwarded uri header and returns an array of forwarded uris
|
28
|
-
#
|
29
|
-
# For example:
|
30
|
-
#
|
31
|
-
# If the HTTP_X_FORWARDED_URI header was set to
|
32
|
-
# '/some/path, /some/other/path, /and/another/path'
|
33
|
-
#
|
34
|
-
# This method would return ['/some/path, '/some/other/path', '/and/another/path']
|
35
|
-
#
|
36
|
-
# Returns an empty array if there aren't any forwarded uris
|
37
|
-
def forwarded_uris
|
38
|
-
env[self.forwarded_uri_header_name].to_s.split(/,\s*/)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,122 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Proxy
|
3
|
-
module ActionController
|
4
|
-
module Base
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
before_filter :set_proxy_relative_url_root
|
8
|
-
around_filter :swap_default_host
|
9
|
-
around_filter :swap_relative_url_root
|
10
|
-
around_filter :swap_session_domain
|
11
|
-
cattr_accessor :original_relative_url_root
|
12
|
-
cattr_accessor :proxy_relative_url_root
|
13
|
-
alias_method_chain :redirect_to, :proxy
|
14
|
-
class << self; delegate :relative_url_root, :relative_url_root=, :to => ::ActionController::AbstractRequest unless ::ActionController::Base.respond_to?(:relative_url_root); end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
protected
|
19
|
-
|
20
|
-
# Calculates the <tt>relative_url_root</tt> by parsing the request path out of the
|
21
|
-
# first forwarded uri
|
22
|
-
#
|
23
|
-
# For example:
|
24
|
-
#
|
25
|
-
# http://example.com/manage/videos/new
|
26
|
-
# gets proxied to
|
27
|
-
# http://your-domain.com/videos/new
|
28
|
-
#
|
29
|
-
# The first forwarded uri would be: /manage/videos/new
|
30
|
-
# and the request path would be: /videos/new
|
31
|
-
#
|
32
|
-
# So this method would return: /manage
|
33
|
-
def parse_proxy_relative_url_root
|
34
|
-
request.forwarded_uris.first.gsub(/#{Regexp.escape(request.path)}$/, '')
|
35
|
-
end
|
36
|
-
|
37
|
-
# Calculates the <tt>session_domain</tt> by parsing the first domain.tld out of the
|
38
|
-
# first forwarded host and prepending a '.'
|
39
|
-
#
|
40
|
-
# For example:
|
41
|
-
#
|
42
|
-
# http://example.com/manage/videos/new
|
43
|
-
# http://some.other-domain.com/videos/new
|
44
|
-
# both get proxied to
|
45
|
-
# http://your-domain.com/videos/new
|
46
|
-
#
|
47
|
-
# The resulting session domain for the first url would be: '.example.com'
|
48
|
-
# The resulting session domain for the second url would be: '.other-domain.com'
|
49
|
-
def parse_session_domain
|
50
|
-
".#{$1}" if /([^\.]+\.[^\.]+)$/.match(request.forwarded_hosts.first)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Forces redirects to use the <tt>default_url_options[:host]</tt> if it exists unless a host
|
54
|
-
# is already set
|
55
|
-
#
|
56
|
-
# For example:
|
57
|
-
#
|
58
|
-
# http://example.com
|
59
|
-
# gets proxied to
|
60
|
-
# http://your-domain.com
|
61
|
-
#
|
62
|
-
# If you have an action that calls <tt>redirect_to new_videos_path</tt>, the example.com domain
|
63
|
-
# would be used instead of your-domain.com
|
64
|
-
def redirect_to_with_proxy(*args)
|
65
|
-
args[0] = request.protocol + ::ActionController::UrlWriter.default_url_options[:host] + args.first if args.first.is_a?(String) && !%r{^\w+://.*}.match(args.first) && !::ActionController::UrlWriter.default_url_options[:host].blank?
|
66
|
-
redirect_to_without_proxy(*args)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Sets the <tt>proxy_relative_url_root</tt> using the +parse_proxy_relative_url_root+ method
|
70
|
-
# to calculate it
|
71
|
-
#
|
72
|
-
# Sets the <tt>proxy_relative_url_root</tt> to nil if there aren't any forwarded uris
|
73
|
-
def set_proxy_relative_url_root
|
74
|
-
::ActionController::Base.proxy_relative_url_root = request.forwarded_uris.empty? ? nil : parse_proxy_relative_url_root
|
75
|
-
end
|
76
|
-
|
77
|
-
# Sets the <tt>default_url_options[:host]</tt> to the first forwarded host if there are any
|
78
|
-
#
|
79
|
-
# The original default host is restored after each request and can be accessed by calling
|
80
|
-
# <tt>ActionController::UrlWriter.default_url_options[:original_host]</tt>
|
81
|
-
def swap_default_host
|
82
|
-
::ActionController::UrlWriter.default_url_options[:original_host] = ::ActionController::UrlWriter.default_url_options[:host]
|
83
|
-
::ActionController::UrlWriter.default_url_options[:host] = request.forwarded_hosts.first unless request.forwarded_hosts.empty?
|
84
|
-
begin
|
85
|
-
yield
|
86
|
-
ensure
|
87
|
-
::ActionController::UrlWriter.default_url_options[:host] = ::ActionController::UrlWriter.default_url_options[:original_host]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Sets the <tt>relative_url_root</tt> to the <tt>proxy_relative_url_root</tt> unless it's nil
|
92
|
-
#
|
93
|
-
# The original relative url root is restored after each request and can be accessed by calling
|
94
|
-
# <tt>ActionController::Base.original_relative_url_root</tt>
|
95
|
-
def swap_relative_url_root
|
96
|
-
::ActionController::Base.original_relative_url_root = ::ActionController::Base.relative_url_root
|
97
|
-
::ActionController::Base.relative_url_root = ::ActionController::Base.proxy_relative_url_root unless ::ActionController::Base.proxy_relative_url_root.nil?
|
98
|
-
begin
|
99
|
-
yield
|
100
|
-
ensure
|
101
|
-
::ActionController::Base.relative_url_root = ::ActionController::Base.original_relative_url_root
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Sets the <tt>session_options[:session_domain]</tt> to the result of the +parse_session_domain+ method
|
106
|
-
# unless there aren't any forwarded hosts
|
107
|
-
#
|
108
|
-
# The original session domain is restored after each request and can be accessed by calling
|
109
|
-
# <tt>ActionController::Base.session_options[:original_session_domain]</tt>
|
110
|
-
def swap_session_domain
|
111
|
-
::ActionController::Base.session_options[:original_session_domain] = ::ActionController::Base.session_options[:session_domain]
|
112
|
-
::ActionController::Base.session_options[:session_domain] = parse_session_domain unless request.forwarded_hosts.empty?
|
113
|
-
begin
|
114
|
-
yield
|
115
|
-
ensure
|
116
|
-
::ActionController::Base.session_options[:session_domain] = ::ActionController::Base.session_options[:original_session_domain]
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Proxy
|
3
|
-
module ActionController
|
4
|
-
module NamedRouteCollection
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval { alias_method_chain :define_url_helper, :proxy }
|
7
|
-
end
|
8
|
-
|
9
|
-
# Named route url helpers (not path helpers) don't seem to work correctly
|
10
|
-
# with forwarded hosts unless we explicitly set the option:
|
11
|
-
#
|
12
|
-
# :only_path => false
|
13
|
-
#
|
14
|
-
# This method only sets that option if it isn't set already
|
15
|
-
def define_url_helper_with_proxy(route, name, kind, options)
|
16
|
-
define_url_helper_without_proxy(route, name, kind, options)
|
17
|
-
if kind == :url
|
18
|
-
selector = url_helper_name(name, kind)
|
19
|
-
@module.module_eval do
|
20
|
-
define_method "#{selector}_with_proxy" do |*args|
|
21
|
-
args << {} unless args.last.is_a? Hash
|
22
|
-
args.last[:only_path] ||= false
|
23
|
-
send "#{selector}_without_proxy", *args
|
24
|
-
end
|
25
|
-
alias_method_chain selector, :proxy
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Proxy
|
3
|
-
module ActionController
|
4
|
-
module UrlRewriter
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval { alias_method_chain :rewrite_url, :proxy }
|
7
|
-
end
|
8
|
-
|
9
|
-
# Adds the default :host option unless already specified
|
10
|
-
#
|
11
|
-
# It will not set the :host option if <tt>options</tt> is not a hash or
|
12
|
-
# if the <tt>ActionController::UrlWriter.default_url_options[:host]</tt> is blank
|
13
|
-
def rewrite_url_with_proxy(options)
|
14
|
-
if options.is_a?(Hash)
|
15
|
-
options[:host] ||= ::ActionController::UrlWriter.default_url_options[:host] unless ::ActionController::UrlWriter.default_url_options[:host].blank?
|
16
|
-
options.delete(:original_host)
|
17
|
-
end
|
18
|
-
rewrite_url_without_proxy(options)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Proxy
|
3
|
-
module ActionView
|
4
|
-
module UrlHelper
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval { alias_method_chain :url_for, :proxy }
|
7
|
-
end
|
8
|
-
|
9
|
-
# Adds the default :host option unless already specified
|
10
|
-
#
|
11
|
-
# It will not set the :host option if <tt>options</tt> is not a hash or
|
12
|
-
# if the <tt>ActionController::UrlWriter.default_url_options[:host]</tt> is blank
|
13
|
-
def url_for_with_proxy(options = {})
|
14
|
-
if options.is_a?(Hash)
|
15
|
-
options[:host] ||= ::ActionController::UrlWriter.default_url_options[:host] unless ::ActionController::UrlWriter.default_url_options[:host].blank?
|
16
|
-
options.delete(:original_host)
|
17
|
-
end
|
18
|
-
url_for_without_proxy(options)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|