sitehub 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +114 -0
  6. data/LICENSE +28 -0
  7. data/README.md +198 -0
  8. data/Rakefile +11 -0
  9. data/lib/sitehub.rb +9 -0
  10. data/lib/sitehub/builder.rb +82 -0
  11. data/lib/sitehub/collection.rb +35 -0
  12. data/lib/sitehub/collection/route_collection.rb +28 -0
  13. data/lib/sitehub/collection/split_route_collection.rb +50 -0
  14. data/lib/sitehub/collection/split_route_collection/split.rb +18 -0
  15. data/lib/sitehub/constants.rb +23 -0
  16. data/lib/sitehub/constants/http_header_keys.rb +25 -0
  17. data/lib/sitehub/constants/rack_http_header_keys.rb +17 -0
  18. data/lib/sitehub/cookie.rb +54 -0
  19. data/lib/sitehub/cookie/attribute.rb +22 -0
  20. data/lib/sitehub/cookie/flag.rb +22 -0
  21. data/lib/sitehub/cookie_rewriting.rb +35 -0
  22. data/lib/sitehub/forward_proxies.rb +50 -0
  23. data/lib/sitehub/forward_proxy.rb +67 -0
  24. data/lib/sitehub/forward_proxy_builder.rb +99 -0
  25. data/lib/sitehub/http_headers.rb +60 -0
  26. data/lib/sitehub/logging.rb +5 -0
  27. data/lib/sitehub/logging/access_logger.rb +74 -0
  28. data/lib/sitehub/logging/error_logger.rb +36 -0
  29. data/lib/sitehub/logging/log_entry.rb +14 -0
  30. data/lib/sitehub/logging/log_stash.rb +10 -0
  31. data/lib/sitehub/logging/log_wrapper.rb +23 -0
  32. data/lib/sitehub/middleware.rb +21 -0
  33. data/lib/sitehub/path_directive.rb +32 -0
  34. data/lib/sitehub/path_directives.rb +21 -0
  35. data/lib/sitehub/request_mapping.rb +43 -0
  36. data/lib/sitehub/resolver.rb +11 -0
  37. data/lib/sitehub/reverse_proxy.rb +53 -0
  38. data/lib/sitehub/rules.rb +13 -0
  39. data/lib/sitehub/string_sanitiser.rb +7 -0
  40. data/lib/sitehub/transaction_id.rb +16 -0
  41. data/lib/sitehub/version.rb +3 -0
  42. data/mem_usage.txt +1584 -0
  43. data/sitehub.gemspec +43 -0
  44. data/spec/basket_spec.rb +30 -0
  45. data/spec/sitehub/builder_spec.rb +203 -0
  46. data/spec/sitehub/collection/route_collection_spec.rb +91 -0
  47. data/spec/sitehub/collection/split_route_collection_spec.rb +111 -0
  48. data/spec/sitehub/collection_spec.rb +40 -0
  49. data/spec/sitehub/cookie/attribute_spec.rb +37 -0
  50. data/spec/sitehub/cookie/flag_spec.rb +27 -0
  51. data/spec/sitehub/cookie_rewriting_spec.rb +67 -0
  52. data/spec/sitehub/cookie_spec.rb +61 -0
  53. data/spec/sitehub/error_handling_spec.rb +21 -0
  54. data/spec/sitehub/forward_proxies_spec.rb +99 -0
  55. data/spec/sitehub/forward_proxy_builder_spec.rb +295 -0
  56. data/spec/sitehub/forward_proxy_spec.rb +138 -0
  57. data/spec/sitehub/http_headers_spec.rb +71 -0
  58. data/spec/sitehub/integration_spec.rb +21 -0
  59. data/spec/sitehub/logging/access_logger_spec.rb +127 -0
  60. data/spec/sitehub/logging/error_logger_spec.rb +80 -0
  61. data/spec/sitehub/logging/log_entry_spec.rb +34 -0
  62. data/spec/sitehub/logging/log_stash_spec.rb +21 -0
  63. data/spec/sitehub/logging/log_wrapper_spec.rb +33 -0
  64. data/spec/sitehub/middleware_spec.rb +69 -0
  65. data/spec/sitehub/path_directive_spec.rb +50 -0
  66. data/spec/sitehub/path_directives_spec.rb +45 -0
  67. data/spec/sitehub/request_mapping_spec.rb +71 -0
  68. data/spec/sitehub/resolver_spec.rb +15 -0
  69. data/spec/sitehub/reverse_proxy_spec.rb +105 -0
  70. data/spec/sitehub/transaction_id_spec.rb +28 -0
  71. data/spec/sitehub_spec.rb +19 -0
  72. data/spec/spec_helper.rb +26 -0
  73. data/spec/support/patch/rack/response.rb +25 -0
  74. data/spec/support/shared_contexts/async_context.rb +69 -0
  75. data/spec/support/shared_contexts/middleware_context.rb +51 -0
  76. data/spec/support/shared_contexts/rack_test_context.rb +12 -0
  77. data/spec/support/shared_contexts/sitehub_context.rb +25 -0
  78. data/spec/support/silent_warnings.rb +5 -0
  79. metadata +359 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8eab93145788748ecc28ef5f14aea92b7595f846
4
+ data.tar.gz: 69f240e1d1d707424bf2e6a07e4791b767441427
5
+ SHA512:
6
+ metadata.gz: a4d317b035c62e2513261872065ef428ae0342fc8a40c5807b8b597908d53b9f288b6aa310cda0ece34b57e6d1d77068040af6ce27c96fc5e22aaf762e0a1f24
7
+ data.tar.gz: 4fbf75725640bbc3dcf06e742bb7b549c444048fb8e4f00d0a64888198d62acd1d0b541a4f225b91928107da2e2bc8b70104144ff8697c8035d03c01aef1a266
@@ -0,0 +1,6 @@
1
+ *.iml
2
+ .idea
3
+ test.rb
4
+ coverage
5
+ .ruby-gemset
6
+ .ruby-version
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sitehub.gemspec
4
+ gemspec
5
+
6
+
@@ -0,0 +1,114 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sitehub (0.4.1)
5
+ em-http-request
6
+ em-synchrony
7
+ faraday
8
+ rack
9
+ rack-fiber_pool
10
+ rack-ssl-enforcer
11
+ thin
12
+ uuid
13
+
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ addressable (2.3.8)
18
+ builder (3.2.2)
19
+ cookiejar (0.3.0)
20
+ crack (0.4.3)
21
+ safe_yaml (~> 1.0.0)
22
+ daemons (1.2.3)
23
+ diff-lcs (1.2.5)
24
+ docile (1.1.5)
25
+ em-http-request (1.1.3)
26
+ addressable (>= 2.3.4)
27
+ cookiejar (<= 0.3.0)
28
+ em-socksify (>= 0.3)
29
+ eventmachine (>= 1.0.3)
30
+ http_parser.rb (>= 0.6.0)
31
+ em-socksify (0.3.1)
32
+ eventmachine (>= 1.0.0.beta.4)
33
+ em-synchrony (1.0.4)
34
+ eventmachine (>= 1.0.0.beta.1)
35
+ eventmachine (1.0.8)
36
+ faraday (0.9.2)
37
+ multipart-post (>= 1.2, < 3)
38
+ geminabox (0.12.4)
39
+ builder
40
+ faraday
41
+ httpclient (>= 2.2.7)
42
+ nesty
43
+ sinatra (>= 1.2.7)
44
+ hashdiff (0.2.3)
45
+ http_parser.rb (0.6.0)
46
+ httpclient (2.7.1)
47
+ json (1.8.3)
48
+ macaddr (1.7.1)
49
+ systemu (~> 2.6.2)
50
+ memory_profiler (0.9.6)
51
+ multipart-post (2.0.0)
52
+ nesty (1.0.2)
53
+ rack (1.6.4)
54
+ rack-fiber_pool (0.9.3)
55
+ rack-protection (1.5.3)
56
+ rack
57
+ rack-ssl-enforcer (0.2.9)
58
+ rack-test (0.6.3)
59
+ rack (>= 1.0)
60
+ rake (10.4.2)
61
+ rspec (3.2.0)
62
+ rspec-core (~> 3.2.0)
63
+ rspec-expectations (~> 3.2.0)
64
+ rspec-mocks (~> 3.2.0)
65
+ rspec-core (3.2.3)
66
+ rspec-support (~> 3.2.0)
67
+ rspec-expectations (3.2.1)
68
+ diff-lcs (>= 1.2.0, < 2.0)
69
+ rspec-support (~> 3.2.0)
70
+ rspec-mocks (3.2.1)
71
+ diff-lcs (>= 1.2.0, < 2.0)
72
+ rspec-support (~> 3.2.0)
73
+ rspec-support (3.2.2)
74
+ ruby-prof (0.15.9)
75
+ safe_yaml (1.0.4)
76
+ simplecov (0.11.1)
77
+ docile (~> 1.1.0)
78
+ json (~> 1.8)
79
+ simplecov-html (~> 0.10.0)
80
+ simplecov-html (0.10.0)
81
+ sinatra (1.4.6)
82
+ rack (~> 1.4)
83
+ rack-protection (~> 1.4)
84
+ tilt (>= 1.3, < 3)
85
+ systemu (2.6.5)
86
+ thin (1.6.4)
87
+ daemons (~> 1.0, >= 1.0.9)
88
+ eventmachine (~> 1.0, >= 1.0.4)
89
+ rack (~> 1.0)
90
+ tilt (2.0.2)
91
+ uuid (2.3.8)
92
+ macaddr (~> 1.0)
93
+ webmock (1.22.5)
94
+ addressable (< 2.4.0)
95
+ crack (>= 0.3.2)
96
+ hashdiff
97
+
98
+ PLATFORMS
99
+ ruby
100
+
101
+ DEPENDENCIES
102
+ bundler (~> 1.5)
103
+ geminabox
104
+ memory_profiler
105
+ rack-test
106
+ rake
107
+ rspec (~> 3.2.0)
108
+ ruby-prof
109
+ simplecov
110
+ sitehub!
111
+ webmock
112
+
113
+ BUNDLED WITH
114
+ 1.11.2
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2015, Ladtech Ltd
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of sitehub nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
@@ -0,0 +1,198 @@
1
+ # SiteHub
2
+ SiteHub is a HTTP proxy written in Ruby with support for A|B testing baked in. SiteHub sits in front of your web application(s) routing all HTTP traffics between it and your users.
3
+
4
+ Wouldn't it be cool to write something like:
5
+ ```ruby
6
+ sitehub = SiteHub.build do
7
+ proxy %r{/catalogue/(.*)} do
8
+ split percentage: 50, url: 'http://version1.com/$1', label: :prototype_1
9
+ split percentage: 50, url: 'http://version2.com/$1', label: :prototype_2
10
+ end
11
+ end
12
+
13
+ run sitehub
14
+ ```
15
+ or
16
+ ```ruby
17
+ user_eligible = proc{} #perform check and return boolean
18
+
19
+ sitehub = SiteHub.build do
20
+ proxy %r{/catalogue/(.*)} do
21
+ route url: 'http://new_catalogue.com/catalogue/$1', label: :new_version, rule: user_eligbile
22
+
23
+ default url: 'http://current_catalogue.com/$1'
24
+ end
25
+ # other proxy definitions ...
26
+ end
27
+
28
+ run sitehub
29
+ ```
30
+
31
+ With SiteHub you can:
32
+ - A|B testing new features
33
+ - Silently release new features - SiteHub can be used to put a new version of you application live but inaccessible to users. This new version can still be accessed by teams to peform final checks before opening up the new version to the public.
34
+ - Modular Web Applications - SiteHub can be used to front discrete applications and present a unified root.
35
+
36
+ ## Installation
37
+ `gem install sitehub`
38
+
39
+ ## Definning a SiteHub
40
+ A SiteHub is a rack application so needs to be passed to the `run` method in your rackup file
41
+
42
+ example config.ru
43
+ ```ruby
44
+ require 'sitehub'
45
+ sitehub = SiteHub.build do
46
+ proxy '/' => 'http://downstream.url.com'
47
+ end
48
+ run sitehub
49
+ ```
50
+
51
+ ## Defining proxies
52
+ Proxies can have either routes or splits defined within them but not both at the same time.
53
+ - Splits - define the percentage chance that a downstream url will be used to proxy a user request.
54
+ - Routes - routes are defined with a rule that determines whether or not a user request can be sent to its downstream url
55
+
56
+ ### Version affinity
57
+ Once a downstream route has been chosen for a given route it is sticky. Meaning that users will stay with a version and not flip flop between them.
58
+
59
+ Sitehub does this by dropping a cookie that holds the route version that a request is sent to.
60
+
61
+ By default the cookie will be given the name `sitehub.recorded_route` and will have the path of the request.
62
+
63
+ #### Overiding the name
64
+ This is done at the top level of your sitehub definition. The name you supply will be used for all sitehub cookies dropped by all proxies.
65
+ ```ruby
66
+ sitehub = SiteHub.build do
67
+ sitehub_cookie_name :your_custom_name
68
+ end
69
+ ```
70
+
71
+ #### Overiding the path
72
+ This is done a proxy by proxy basis
73
+ ```ruby
74
+ sitehub = SiteHub.build do
75
+ proxy '/' do
76
+ sitehub_cookie_path '/your/path'
77
+ #splits/routes defined here
78
+ end
79
+ end
80
+ ```
81
+ **Caution:**
82
+ By default sitehub is going to use the path of the request. If you have used a regular expression to define a proxy, this will be different for each unique request that is made.
83
+
84
+ e.g. for the following example, calls to /path1, and /path2 would each be given a cookie meaning that users could flip between different version for the same proxy definition. In this case you are definately going to want to set the `sitehub_cookie_path` to keep things consistent.
85
+
86
+ ```ruby
87
+ sitehub = SiteHub.build do
88
+ proxy %r{/*} do
89
+ #splits/routes defined here
90
+ end
91
+ end
92
+ ```
93
+
94
+ ### Routes with Rules
95
+ Define a route inside a proxy defintion as follows
96
+ ```ruby
97
+ sitehub = SiteHub.build do
98
+ proxy '/catalogue' do
99
+ route url: 'http://new_catalogue.com/catalogue', label: :new_version, rule: user_eligbile
100
+ # ...
101
+ end
102
+ end
103
+ ```
104
+ Rules must be an object that responds to call with a single parameter that returns a boolean. True means that the rule applies and false means that it does not.
105
+
106
+ The parameter passed to call is the request environment hash. This lets you write things like:
107
+ ```ruby
108
+ has_special_parameter = proc do |env|
109
+ Rack::Request.new(env).params.include?(:special_param)
110
+ end
111
+ ```
112
+
113
+ ### Splits
114
+ Splits are defined as follows
115
+ ```ruby
116
+ sitehub = SiteHub.build do
117
+ proxy '/catalogue' do
118
+ split percentage: 50, url: 'http://version1.com', label: :prototype_1
119
+ split percentage: 50, url: 'http://version2.com', label: :prototype_2
120
+ end
121
+ end
122
+ ```
123
+ Split percentages must add up to 100% unless a default is defined.
124
+
125
+ ### Default routes
126
+ When defining either Splits or Routes a default can be defined. Defaults are used as a fallback if a route with a rule that applies can't be found or a split can't be chosen on the first time of trying (This can happen when the splits don't add up to 100%).
127
+ ```ruby
128
+ sitehub = SiteHub.build do
129
+ proxy '/catalogue' do
130
+ route url: 'http://new_catalogue.com/catalogue', label: :new_version, rule: a_rule
131
+ default url: 'http://current_catalogue.com'
132
+ end
133
+ end
134
+ ```
135
+ ### Labels
136
+ Splits and Routes must be defined with a label. Within a proxy defintion, this label must be unique. This is the value that SiteHub will use to identify the version of a downstream url that a user should stick to once it has been selected.
137
+
138
+ ### Matching
139
+ Proxy can be defined to capture specific paths using a literal string or be defined to have a broader appeal using regexs
140
+ ```ruby
141
+ sitehub = SiteHub.build do
142
+ proxy '/catalogue' => 'http://downstream.catalogue.com'
143
+ proxy %r{/orders/*} => 'http://downstream.orders.com'
144
+ end
145
+ ```
146
+
147
+ ### Substitution
148
+ Portions of the request path can be captured and passed downstream by specifying capture groups your path regular expression.
149
+ ```ruby
150
+ sitehub = SiteHub.build do
151
+ proxy %r{'/orders/(.*)} => 'http://downstream.orders.com/$1'
152
+ end
153
+ ```
154
+
155
+ ## Using middleware
156
+ You can `use` middleware in conjunction with a particular proxy definition or the SiteHub as a whole.
157
+ ### Proxy specifc middleware
158
+ ```ruby
159
+ sitehub = SiteHub.build do
160
+ proxy '/catalogue/(.*)' do
161
+ use AuthenticationMiddlware
162
+ end
163
+ end
164
+ ```
165
+ In this example, only requests received by the proxy definition will be made to go through the AuthenticationMiddleware
166
+
167
+ ### SiteHub wide middlware
168
+ ```ruby
169
+ sitehub = SiteHub.build do
170
+ use AuthenticationMiddleware
171
+ proxy '/catalogue' => 'http://downstream.catalogue.com'
172
+ proxy '/orders' => 'http://downstream.orders.com'
173
+ end
174
+ ```
175
+ In this example, all requests handled by the SiteHub will go through the AuthenticationMiddleware
176
+ ### Reverse Proxying
177
+ In order to ensure that you applications stay firmly behind your SiteHub, you are going need to ensure that any responses that they return have are rewritten to remove references to your downstream URLs. SiteHub does this for the `Location` header (set for redirects) and will soon do it for the [Content-Location](https://github.com/bskyb-commerce/sitehub/issues/8) header also.
178
+ ```ruby
179
+ sitehub = SiteHub.build do
180
+ proxy %r{/orders/(.*)} => 'http://downstream.orders.com/$1'
181
+ reverse_proxy %r{http://downstream.orders.com/(.*)} => '/orders/$1'
182
+ end
183
+ ```
184
+ *Note* The above example also performs substitution from the downstream URL in to the upstream mapping. This is not mandatory
185
+
186
+ ## SiteHub Transaction ID
187
+ SiteHub introduces a custom header to downstream requests called `sitehub_transaction_id` that is unique to every request. The idea is that if a request is made from the downstream system to another then this header should be passed on also. If access/errors in each system are logged along with this id, then tracing things in distributed systems will become easier.
188
+
189
+ The transaction id, if passed on through out, could also be used for request scoped caching.
190
+
191
+ ## Logging
192
+ By default SiteHub will log requests and errors to STDOUT and STDERR respectively. You can overide this with your own logging devices. For example you may want to send requests and errors to syslog. Just make sure that your logger object responds to `<<` or `write` and SiteHub will do the rest.
193
+ ```ruby
194
+ sitehub = SiteHub.build do
195
+ access_logger YourLogger.new
196
+ error_logger YourLogger.new
197
+ end
198
+ ```
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do
8
+ ENV['coverage'] = 'true'
9
+ end
10
+
11
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ require 'sitehub/constants'
2
+ require 'sitehub/builder'
3
+ class SiteHub
4
+ class << self
5
+ def build &block
6
+ Builder.new(&block).build
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ require 'sitehub/forward_proxies'
2
+ require 'sitehub/transaction_id'
3
+ require 'sitehub/middleware'
4
+ require 'sitehub/forward_proxy_builder'
5
+ require 'sitehub/reverse_proxy'
6
+ require 'rack/ssl-enforcer'
7
+ require 'sitehub/logging'
8
+ require 'rack/fiber_pool'
9
+ require 'logger'
10
+
11
+
12
+ class SiteHub
13
+
14
+ class InvalidProxyDefinitionException < Exception;
15
+ end
16
+ class Builder
17
+
18
+ attr_reader :sitehub, :forward_proxies, :reverse_proxies
19
+
20
+ include Middleware
21
+
22
+ def force_ssl except: []
23
+ @force_ssl = true
24
+ @ssl_exclusions = except
25
+ end
26
+
27
+ def initialize(&block)
28
+ @forward_proxies = ForwardProxies.new
29
+ @reverse_proxies = {}
30
+ instance_eval &block if block
31
+ end
32
+
33
+ def access_logger logger=nil
34
+ return @access_logger unless logger
35
+ @access_logger = logger
36
+ end
37
+
38
+ def error_logger logger=nil
39
+ return @error_logger unless logger
40
+ @error_logger = logger
41
+ end
42
+
43
+ def sitehub_cookie_name name=nil
44
+ @sitehub_cookie_name ||= RECORDED_ROUTES_COOKIE
45
+
46
+ return @sitehub_cookie_name unless name
47
+ @sitehub_cookie_name = name
48
+ end
49
+
50
+ def build
51
+ forward_proxies.init
52
+ use ReverseProxy, reverse_proxies
53
+ use TransactionId
54
+ use Logging::AccessLogger, access_logger || ::Logger.new(STDOUT)
55
+ use Logging::ErrorLogger, error_logger || ::Logger.new(STDERR)
56
+ use Rack::FiberPool
57
+ use Rack::SslEnforcer, except: @ssl_exclusions if @force_ssl
58
+ middlewares.reverse!
59
+
60
+ apply_middleware(forward_proxies)
61
+ end
62
+
63
+ def proxy opts={}, &block
64
+ args = {sitehub_cookie_name: sitehub_cookie_name}
65
+
66
+ if opts.is_a?(Hash)
67
+ mapped_path = opts.keys.first
68
+ url = opts.values.first
69
+ args.merge!(url: url, mapped_path: mapped_path)
70
+ else
71
+ args.merge!(mapped_path: opts)
72
+ end
73
+
74
+ forward_proxies << ForwardProxyBuilder.new(args, &block)
75
+ end
76
+
77
+ def reverse_proxy hash
78
+ reverse_proxies.merge!(hash)
79
+ end
80
+ end
81
+
82
+ end