sitehub 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +114 -0
- data/LICENSE +28 -0
- data/README.md +198 -0
- data/Rakefile +11 -0
- data/lib/sitehub.rb +9 -0
- data/lib/sitehub/builder.rb +82 -0
- data/lib/sitehub/collection.rb +35 -0
- data/lib/sitehub/collection/route_collection.rb +28 -0
- data/lib/sitehub/collection/split_route_collection.rb +50 -0
- data/lib/sitehub/collection/split_route_collection/split.rb +18 -0
- data/lib/sitehub/constants.rb +23 -0
- data/lib/sitehub/constants/http_header_keys.rb +25 -0
- data/lib/sitehub/constants/rack_http_header_keys.rb +17 -0
- data/lib/sitehub/cookie.rb +54 -0
- data/lib/sitehub/cookie/attribute.rb +22 -0
- data/lib/sitehub/cookie/flag.rb +22 -0
- data/lib/sitehub/cookie_rewriting.rb +35 -0
- data/lib/sitehub/forward_proxies.rb +50 -0
- data/lib/sitehub/forward_proxy.rb +67 -0
- data/lib/sitehub/forward_proxy_builder.rb +99 -0
- data/lib/sitehub/http_headers.rb +60 -0
- data/lib/sitehub/logging.rb +5 -0
- data/lib/sitehub/logging/access_logger.rb +74 -0
- data/lib/sitehub/logging/error_logger.rb +36 -0
- data/lib/sitehub/logging/log_entry.rb +14 -0
- data/lib/sitehub/logging/log_stash.rb +10 -0
- data/lib/sitehub/logging/log_wrapper.rb +23 -0
- data/lib/sitehub/middleware.rb +21 -0
- data/lib/sitehub/path_directive.rb +32 -0
- data/lib/sitehub/path_directives.rb +21 -0
- data/lib/sitehub/request_mapping.rb +43 -0
- data/lib/sitehub/resolver.rb +11 -0
- data/lib/sitehub/reverse_proxy.rb +53 -0
- data/lib/sitehub/rules.rb +13 -0
- data/lib/sitehub/string_sanitiser.rb +7 -0
- data/lib/sitehub/transaction_id.rb +16 -0
- data/lib/sitehub/version.rb +3 -0
- data/mem_usage.txt +1584 -0
- data/sitehub.gemspec +43 -0
- data/spec/basket_spec.rb +30 -0
- data/spec/sitehub/builder_spec.rb +203 -0
- data/spec/sitehub/collection/route_collection_spec.rb +91 -0
- data/spec/sitehub/collection/split_route_collection_spec.rb +111 -0
- data/spec/sitehub/collection_spec.rb +40 -0
- data/spec/sitehub/cookie/attribute_spec.rb +37 -0
- data/spec/sitehub/cookie/flag_spec.rb +27 -0
- data/spec/sitehub/cookie_rewriting_spec.rb +67 -0
- data/spec/sitehub/cookie_spec.rb +61 -0
- data/spec/sitehub/error_handling_spec.rb +21 -0
- data/spec/sitehub/forward_proxies_spec.rb +99 -0
- data/spec/sitehub/forward_proxy_builder_spec.rb +295 -0
- data/spec/sitehub/forward_proxy_spec.rb +138 -0
- data/spec/sitehub/http_headers_spec.rb +71 -0
- data/spec/sitehub/integration_spec.rb +21 -0
- data/spec/sitehub/logging/access_logger_spec.rb +127 -0
- data/spec/sitehub/logging/error_logger_spec.rb +80 -0
- data/spec/sitehub/logging/log_entry_spec.rb +34 -0
- data/spec/sitehub/logging/log_stash_spec.rb +21 -0
- data/spec/sitehub/logging/log_wrapper_spec.rb +33 -0
- data/spec/sitehub/middleware_spec.rb +69 -0
- data/spec/sitehub/path_directive_spec.rb +50 -0
- data/spec/sitehub/path_directives_spec.rb +45 -0
- data/spec/sitehub/request_mapping_spec.rb +71 -0
- data/spec/sitehub/resolver_spec.rb +15 -0
- data/spec/sitehub/reverse_proxy_spec.rb +105 -0
- data/spec/sitehub/transaction_id_spec.rb +28 -0
- data/spec/sitehub_spec.rb +19 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/patch/rack/response.rb +25 -0
- data/spec/support/shared_contexts/async_context.rb +69 -0
- data/spec/support/shared_contexts/middleware_context.rb +51 -0
- data/spec/support/shared_contexts/rack_test_context.rb +12 -0
- data/spec/support/shared_contexts/sitehub_context.rb +25 -0
- data/spec/support/silent_warnings.rb +5 -0
- metadata +359 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'uuid'
|
2
|
+
require_relative 'collection/split_route_collection'
|
3
|
+
require_relative 'rules'
|
4
|
+
require_relative 'resolver'
|
5
|
+
require_relative 'collection/route_collection'
|
6
|
+
require_relative 'middleware'
|
7
|
+
|
8
|
+
class SiteHub
|
9
|
+
|
10
|
+
class ForwardProxyBuilder
|
11
|
+
include Middleware
|
12
|
+
include Rules, Resolver
|
13
|
+
|
14
|
+
class InvalidDefinitionException < Exception;
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :mapped_path, :default_proxy, :routes, :middlewares, :splits, :sitehub_cookie_name
|
18
|
+
|
19
|
+
def initialize(url: nil, mapped_path:, rule:nil, sitehub_cookie_name: nil, &block)
|
20
|
+
@mapped_path = mapped_path
|
21
|
+
@middlewares = []
|
22
|
+
@splits = Collection::SplitRouteCollection.new
|
23
|
+
@routes = Collection::RouteCollection.new
|
24
|
+
@sitehub_cookie_name = sitehub_cookie_name
|
25
|
+
rule(rule) if rule
|
26
|
+
default(url: url) if url
|
27
|
+
if block
|
28
|
+
instance_eval(&block)
|
29
|
+
raise InvalidDefinitionException unless valid?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?
|
34
|
+
return true if @default_proxy
|
35
|
+
endpoints.valid?
|
36
|
+
end
|
37
|
+
|
38
|
+
def endpoints collection=nil
|
39
|
+
return @endpoints || Collection::RouteCollection.new unless collection
|
40
|
+
|
41
|
+
raise InvalidDefinitionException, 'you cant register routes and splits at the same level' if @endpoints && @endpoints != collection
|
42
|
+
@endpoints = collection
|
43
|
+
end
|
44
|
+
|
45
|
+
def split(percentage:, url:, label:, &block)
|
46
|
+
endpoints(splits)
|
47
|
+
endpoints.add label.to_sym, forward_proxy(label: label, url: url), percentage
|
48
|
+
end
|
49
|
+
|
50
|
+
def route url:nil, label:nil, rule: nil, &block
|
51
|
+
endpoints(routes)
|
52
|
+
|
53
|
+
if block
|
54
|
+
raise InvalidDefinitionException, 'rule must be specified when supplying a block'unless rule
|
55
|
+
builder = self.class.new(mapped_path: mapped_path, rule: rule, &block).build
|
56
|
+
endpoints.add UUID.generate(:compact), builder
|
57
|
+
else
|
58
|
+
endpoints.add label.to_sym, forward_proxy(url: url, label: label, rule: rule)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def default(url:)
|
63
|
+
@default_proxy = forward_proxy(label: :default, url: url)
|
64
|
+
end
|
65
|
+
|
66
|
+
def sitehub_cookie_path path=nil
|
67
|
+
return @sitehub_cookie_path unless path
|
68
|
+
@sitehub_cookie_path = path
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def build
|
73
|
+
endpoints.transform do |proxy|
|
74
|
+
apply_middleware(proxy).tap do |wrapped_proxy|
|
75
|
+
wrapped_proxy.extend(Rules)
|
76
|
+
wrapped_proxy.extend(Resolver) unless wrapped_proxy.is_a?(Resolver)
|
77
|
+
wrapped_proxy.rule(proxy.rule)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@default_proxy = apply_middleware(default_proxy) if default_proxy
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def resolve(id: nil, env:)
|
85
|
+
id = id.to_s.to_sym
|
86
|
+
endpoints[id] || endpoints.resolve(env: env) || default_proxy
|
87
|
+
end
|
88
|
+
|
89
|
+
def == other
|
90
|
+
other.is_a?(ForwardProxyBuilder) && self.default_proxy == other.default_proxy && self.endpoints == other.endpoints
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def forward_proxy(label:, url:, rule: nil)
|
95
|
+
ForwardProxy.new(url: url, id: label.to_sym, mapped_path: mapped_path, sitehub_cookie_path: sitehub_cookie_path, sitehub_cookie_name: sitehub_cookie_name, rule: rule)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'sitehub/constants'
|
2
|
+
class SiteHub
|
3
|
+
module HttpHeaders
|
4
|
+
include Constants::HttpHeaderKeys
|
5
|
+
include Constants
|
6
|
+
|
7
|
+
HTTP_OR_SSL_PORT = /:80(?!\d+)|:443/
|
8
|
+
HTTP_PREFIX = /^HTTP_/
|
9
|
+
RACK_HTTP_HEADER_ID = /#{HTTP_PREFIX.source}[A-Z_]+$/
|
10
|
+
COMMAND_FOLLOWED_BY_SPACES = /,\s+/
|
11
|
+
|
12
|
+
def split_field(f)
|
13
|
+
f ? f.split(COMMAND_FOLLOWED_BY_SPACES).collect { |i| i.downcase } : []
|
14
|
+
end
|
15
|
+
|
16
|
+
def sanitise_headers(src)
|
17
|
+
|
18
|
+
sanitised_headers = {}
|
19
|
+
|
20
|
+
connections = split_field(src[CONNECTION_HEADER])
|
21
|
+
src.each do |key, value|
|
22
|
+
key = key.downcase.gsub(UNDERSCORE, HYPHEN)
|
23
|
+
if HopByHop.member?(key) ||
|
24
|
+
connections.member?(key) ||
|
25
|
+
ShouldNotTransfer.member?(key)
|
26
|
+
next
|
27
|
+
end
|
28
|
+
sanitised_headers[key] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
sanitised_headers[LOCATION_HEADER].gsub!(HTTP_OR_SSL_PORT, EMPTY_STRING) if sanitised_headers[LOCATION_HEADER]
|
32
|
+
|
33
|
+
sanitised_headers
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
def extract_http_headers(env)
|
39
|
+
headers = env.reject do |k, v|
|
40
|
+
!RackHttpHeaderKeys::HTTP_HEADER_FILTER_EXCEPTIONS.include?(k.to_s.upcase) && (!(RACK_HTTP_HEADER_ID === k) || v.nil?)
|
41
|
+
end.map do |k, v|
|
42
|
+
[reconstruct_header_name(k), v]
|
43
|
+
end.inject(Rack::Utils::HeaderHash.new) do |hash, k_v|
|
44
|
+
k, v = k_v
|
45
|
+
hash[k] = v
|
46
|
+
hash
|
47
|
+
end
|
48
|
+
|
49
|
+
x_forwarded_for = (headers[X_FORWARDED_FOR_HEADER].to_s.split(COMMAND_FOLLOWED_BY_SPACES) << env[RackHttpHeaderKeys::REMOTE_ADDRESS_ENV_KEY]).join(COMMA_WITH_SPACE)
|
50
|
+
|
51
|
+
headers.merge!(X_FORWARDED_FOR_HEADER => x_forwarded_for)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
def reconstruct_header_name(name)
|
57
|
+
name.sub(HTTP_PREFIX, EMPTY_STRING).gsub(UNDERSCORE, HYPHEN)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rack/commonlogger'
|
3
|
+
require_relative 'log_wrapper'
|
4
|
+
require 'sitehub/constants'
|
5
|
+
|
6
|
+
#Very heavily based on Rack::CommonLogger
|
7
|
+
class SiteHub
|
8
|
+
module Logging
|
9
|
+
class AccessLogger
|
10
|
+
attr_reader :logger, :start_time
|
11
|
+
|
12
|
+
include Constants
|
13
|
+
|
14
|
+
FORMAT = %{%s - %s [%s] transaction_id:%s: "%s %s%s => %s %s" %d %s %0.4f\n}.freeze
|
15
|
+
PATH_INFO = RackHttpHeaderKeys::PATH_INFO
|
16
|
+
REQUEST_METHOD = RackHttpHeaderKeys::REQUEST_METHOD
|
17
|
+
SCRIPT_NAME = RackHttpHeaderKeys::SCRIPT_NAME
|
18
|
+
QUERY_STRING = RackHttpHeaderKeys::QUERY_STRING
|
19
|
+
X_FORWARDED_FOR = RackHttpHeaderKeys::X_FORWARDED_FOR
|
20
|
+
REMOTE_ADDR = RackHttpHeaderKeys::REMOTE_ADDR
|
21
|
+
HTTP_VERSION = RackHttpHeaderKeys::HTTP_VERSION
|
22
|
+
REMOTE_USER = RackHttpHeaderKeys::REMOTE_USER
|
23
|
+
TRANSACTION_ID = RackHttpHeaderKeys::TRANSACTION_ID
|
24
|
+
CONTENT_LENGTH = HttpHeaderKeys::CONTENT_LENGTH
|
25
|
+
ZERO_STRING = '0'
|
26
|
+
STATUS_RANGE = 0..3
|
27
|
+
|
28
|
+
def initialize app, logger = ::Logger.new(STDOUT)
|
29
|
+
@app = app
|
30
|
+
@logger = LogWrapper.new(logger)
|
31
|
+
end
|
32
|
+
|
33
|
+
def call env
|
34
|
+
start_time = Time.now
|
35
|
+
|
36
|
+
@app.call(env).tap do |response|
|
37
|
+
status, headers, body = response.to_a
|
38
|
+
log env, status, headers, env[REQUEST_MAPPING], start_time
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def log(env, status, header, mapped_request, began_at)
|
43
|
+
now = Time.now
|
44
|
+
length = extract_content_length(header)
|
45
|
+
|
46
|
+
|
47
|
+
msg = log_template % [
|
48
|
+
env[X_FORWARDED_FOR] || env[REMOTE_ADDR] || HYPHEN,
|
49
|
+
env[REMOTE_USER] || "-",
|
50
|
+
now.strftime(TIME_STAMP_FORMAT),
|
51
|
+
env[TRANSACTION_ID],
|
52
|
+
env[REQUEST_METHOD],
|
53
|
+
env[PATH_INFO],
|
54
|
+
env[QUERY_STRING].empty? ? EMPTY_STRING : QUESTION_MARK+env[QUERY_STRING],
|
55
|
+
mapped_request.mapped_url.to_s,
|
56
|
+
env[HTTP_VERSION],
|
57
|
+
status.to_s[STATUS_RANGE],
|
58
|
+
length,
|
59
|
+
now - began_at]
|
60
|
+
|
61
|
+
logger.write(msg)
|
62
|
+
end
|
63
|
+
|
64
|
+
def log_template
|
65
|
+
FORMAT
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_content_length(headers)
|
69
|
+
value = headers[CONTENT_LENGTH] or return HYPHEN
|
70
|
+
value.to_s == ZERO_STRING ? HYPHEN : value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'sitehub/constants'
|
3
|
+
require_relative 'log_wrapper'
|
4
|
+
require_relative 'log_stash'
|
5
|
+
class SiteHub
|
6
|
+
module Logging
|
7
|
+
class ErrorLogger
|
8
|
+
include Constants
|
9
|
+
LOG_TEMPLATE = '[%s] ERROR: %s - %s'
|
10
|
+
|
11
|
+
|
12
|
+
attr_reader :logger
|
13
|
+
|
14
|
+
def initialize(app, logger = Logger.new(STDERR))
|
15
|
+
@app = app
|
16
|
+
@logger = LogWrapper.new(logger)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call env
|
20
|
+
env[ERRORS] ||= LogStash.new
|
21
|
+
@app.call(env).tap do
|
22
|
+
unless env[ERRORS].empty?
|
23
|
+
messages = env[ERRORS].collect { |log_entry| log_message(error: log_entry.message, transaction_id: env[RackHttpHeaderKeys::TRANSACTION_ID]) }
|
24
|
+
|
25
|
+
logger.write(messages.join(NEW_LINE))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def log_message(error:, transaction_id:)
|
31
|
+
LOG_TEMPLATE % [Time.now.strftime(TIME_STAMP_FORMAT), transaction_id, error]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class SiteHub
|
2
|
+
module Logging
|
3
|
+
class LogEntry
|
4
|
+
attr_reader :message, :time
|
5
|
+
def initialize message, time=Time.now
|
6
|
+
@message, @time = message, time
|
7
|
+
end
|
8
|
+
|
9
|
+
def == other
|
10
|
+
other.is_a?(LogEntry) && self.message == other.message && self.time == other.time
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SiteHub
|
2
|
+
module Logging
|
3
|
+
class LogWrapper
|
4
|
+
attr_reader :logger
|
5
|
+
|
6
|
+
def initialize logger
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def write msg
|
11
|
+
if logger.respond_to?(:<<)
|
12
|
+
logger << msg
|
13
|
+
elsif logger.respond_to?(:write)
|
14
|
+
logger.write(msg)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def == other
|
19
|
+
other.is_a?(LogWrapper) && self.logger == other.logger
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class SiteHub
|
2
|
+
module Middleware
|
3
|
+
def middlewares
|
4
|
+
@middleware ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def use(middleware_clazz, *args, &block)
|
8
|
+
middlewares << [middleware_clazz, args, block|| proc{}]
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply_middleware(forward_proxy)
|
12
|
+
middlewares.reverse.inject(forward_proxy) do |app, middleware_def|
|
13
|
+
middleware = middleware_def[0]
|
14
|
+
args = middleware_def[1] || []
|
15
|
+
block = middleware_def[2] || proc {}
|
16
|
+
|
17
|
+
middleware.new(app, *args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SiteHub
|
2
|
+
class PathDirective
|
3
|
+
attr_reader :matcher, :path_template
|
4
|
+
|
5
|
+
def initialize matcher, path_template
|
6
|
+
@matcher, @path_template = matcher, path_template
|
7
|
+
end
|
8
|
+
|
9
|
+
def match? url
|
10
|
+
!!matcher.match(url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def path_template
|
14
|
+
@path_template.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def apply url
|
18
|
+
url_components = matcher.match(url).captures
|
19
|
+
|
20
|
+
path_template.tap do |p|
|
21
|
+
url_components.each_with_index do |m, index|
|
22
|
+
p.gsub!(RequestMapping::CAPTURE_GROUP_REFERENCE % (index+1), m)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def == other
|
29
|
+
other.is_a?(PathDirective) && self.matcher == other.matcher && self.path_template == other.path_template
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'sitehub/path_directive'
|
2
|
+
class SiteHub
|
3
|
+
class PathDirectives < Array
|
4
|
+
def initialize map={}
|
5
|
+
enriched = map.map do |array|
|
6
|
+
matcher,path_template = array.first, array.last
|
7
|
+
|
8
|
+
matcher = matcher.is_a?(Regexp) ? matcher : %r{#{matcher}}
|
9
|
+
PathDirective.new(matcher, path_template)
|
10
|
+
end
|
11
|
+
|
12
|
+
super enriched
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(url)
|
16
|
+
super() do |directive|
|
17
|
+
directive.match?(url)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'sitehub/constants'
|
2
|
+
class SiteHub
|
3
|
+
class RequestMapping
|
4
|
+
attr_reader :source_url, :mapped_url, :mapped_path
|
5
|
+
|
6
|
+
BASE_URL_MATCHER = %r{^\w+://[\w+\.-]+(:\d+)?}
|
7
|
+
CAPTURE_GROUP_REFERENCE='$%s'
|
8
|
+
USER_SUPPLIED_CAPTURE = 1..-1
|
9
|
+
|
10
|
+
def initialize(source_url:, downstream_url: EMPTY_STRING, mapped_url: EMPTY_STRING, mapped_path:)
|
11
|
+
@source_url, @mapped_url = source_url, mapped_url.dup
|
12
|
+
@mapped_path = mapped_path.is_a?(Regexp) ? mapped_path : Regexp.new(mapped_path)
|
13
|
+
@downstream_url = downstream_url
|
14
|
+
end
|
15
|
+
|
16
|
+
def cookie_path
|
17
|
+
if mapped_path.is_a?(Regexp)
|
18
|
+
mapped_path.source[/^(.*)?\(/,1].gsub(/\/$/, '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def computed_uri
|
24
|
+
@computed_uri ||= begin
|
25
|
+
url_components = url_scanner_regex.match(source_url).captures[USER_SUPPLIED_CAPTURE]
|
26
|
+
mapped_url.tap do |url|
|
27
|
+
url_components.each_with_index do |match, index|
|
28
|
+
url.gsub!(CAPTURE_GROUP_REFERENCE % (index+1), match)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def == other
|
35
|
+
other.is_a?(RequestMapping) && source_url == other.source_url && mapped_url == other.mapped_url && mapped_path == other.mapped_path
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def url_scanner_regex
|
40
|
+
%r{#{BASE_URL_MATCHER.source}#{mapped_path.source}}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|