sitehub 0.4.2 → 0.4.3
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +17 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +15 -1
- data/README.md +23 -0
- data/Rakefile +3 -9
- data/circle.yml +6 -0
- data/lib/sitehub/builder.rb +19 -19
- data/lib/sitehub/collection/route_collection.rb +7 -9
- data/lib/sitehub/collection/split_route_collection/split.rb +7 -5
- data/lib/sitehub/collection/split_route_collection.rb +13 -10
- data/lib/sitehub/collection.rb +26 -21
- data/lib/sitehub/constants/http_header_keys.rb +2 -6
- data/lib/sitehub/constants/rack_http_header_keys.rb +2 -2
- data/lib/sitehub/constants.rb +3 -3
- data/lib/sitehub/cookie/attribute.rb +4 -4
- data/lib/sitehub/cookie/flag.rb +4 -5
- data/lib/sitehub/cookie.rb +10 -14
- data/lib/sitehub/cookie_rewriting.rb +11 -13
- data/lib/sitehub/forward_proxies.rb +12 -13
- data/lib/sitehub/forward_proxy.rb +38 -24
- data/lib/sitehub/forward_proxy_builder.rb +38 -21
- data/lib/sitehub/http_headers.rb +39 -26
- data/lib/sitehub/logging/access_logger.rb +39 -35
- data/lib/sitehub/logging/error_logger.rb +7 -7
- data/lib/sitehub/logging/log_entry.rb +6 -5
- data/lib/sitehub/logging/log_stash.rb +2 -2
- data/lib/sitehub/logging/log_wrapper.rb +5 -5
- data/lib/sitehub/logging.rb +1 -1
- data/lib/sitehub/middleware.rb +2 -2
- data/lib/sitehub/path_directive.rb +10 -10
- data/lib/sitehub/path_directives.rb +5 -4
- data/lib/sitehub/request_mapping.rb +13 -11
- data/lib/sitehub/resolver.rb +5 -6
- data/lib/sitehub/reverse_proxy.rb +16 -12
- data/lib/sitehub/rules.rb +2 -2
- data/lib/sitehub/string_sanitiser.rb +1 -1
- data/lib/sitehub/transaction_id.rb +3 -3
- data/lib/sitehub/version.rb +1 -1
- data/lib/sitehub.rb +1 -1
- data/sitehub.gemspec +27 -29
- data/spec/sitehub/builder_spec.rb +18 -20
- data/spec/sitehub/collection/route_collection_spec.rb +16 -14
- data/spec/sitehub/collection/split_route_collection_spec.rb +8 -10
- data/spec/sitehub/collection_spec.rb +7 -7
- data/spec/sitehub/cookie/attribute_spec.rb +3 -3
- data/spec/sitehub/cookie/flag_spec.rb +2 -2
- data/spec/sitehub/cookie_rewriting_spec.rb +15 -12
- data/spec/sitehub/cookie_spec.rb +7 -18
- data/spec/sitehub/error_handling_spec.rb +2 -3
- data/spec/sitehub/forward_proxies_spec.rb +16 -12
- data/spec/sitehub/forward_proxy_builder_spec.rb +53 -30
- data/spec/sitehub/forward_proxy_spec.rb +26 -22
- data/spec/sitehub/http_headers_spec.rb +17 -18
- data/spec/sitehub/integration_spec.rb +4 -5
- data/spec/sitehub/logging/access_logger_spec.rb +25 -24
- data/spec/sitehub/logging/error_logger_spec.rb +5 -7
- data/spec/sitehub/logging/log_entry_spec.rb +2 -5
- data/spec/sitehub/logging/log_stash_spec.rb +1 -3
- data/spec/sitehub/logging/log_wrapper_spec.rb +0 -4
- data/spec/sitehub/middleware_spec.rb +1 -5
- data/spec/sitehub/path_directive_spec.rb +4 -7
- data/spec/sitehub/path_directives_spec.rb +6 -7
- data/spec/sitehub/request_mapping_spec.rb +2 -5
- data/spec/sitehub/resolver_spec.rb +1 -1
- data/spec/sitehub/reverse_proxy_spec.rb +37 -31
- data/spec/sitehub/transaction_id_spec.rb +3 -3
- data/spec/sitehub_spec.rb +2 -4
- data/spec/support/async/callback.rb +11 -0
- data/spec/support/async/middleware.rb +25 -0
- data/spec/support/async/response_handler.rb +16 -0
- data/spec/support/async.rb +4 -0
- data/spec/support/patch/rack/response.rb +13 -21
- data/spec/support/shared_contexts/async_context.rb +3 -58
- data/spec/support/shared_contexts/middleware_context.rb +15 -17
- data/spec/support/shared_contexts/rack_test_context.rb +3 -3
- data/spec/support/shared_contexts/sitehub_context.rb +9 -4
- data/spec/support/silent_warnings.rb +2 -3
- data/tasks/code_quality.rake +15 -0
- data/tasks/gem_tasks.rake +1 -0
- data/tasks/support/console.rb +7 -0
- data/tasks/testing.rake +4 -0
- data/tasks/util_tasks.rake +7 -0
- metadata +27 -3
- data/spec/basket_spec.rb +0 -30
@@ -7,9 +7,8 @@ require 'em-http'
|
|
7
7
|
|
8
8
|
class SiteHub
|
9
9
|
class ForwardProxies
|
10
|
-
|
11
10
|
NOT_FOUND = Rack::Response.new(['page not found'], 404, {})
|
12
|
-
def call
|
11
|
+
def call(env)
|
13
12
|
source_request = Rack::Request.new(env)
|
14
13
|
|
15
14
|
forward_proxy = mapped_route(path: source_request.path, request: source_request)
|
@@ -19,32 +18,32 @@ class SiteHub
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def init
|
22
|
-
forward_proxies.values.each
|
21
|
+
forward_proxies.values.each(&:build)
|
23
22
|
self
|
24
23
|
end
|
25
24
|
|
26
|
-
def <<
|
25
|
+
def <<(route)
|
27
26
|
forward_proxies[route.mapped_path] = route
|
28
27
|
end
|
29
28
|
|
30
29
|
def mapped_route(path:, request:)
|
30
|
+
fwd_proxy_builder = forward_proxies[mapping(path)]
|
31
|
+
fwd_proxy_builder ? fwd_proxy_builder.resolve(id: request.cookies[RECORDED_ROUTES_COOKIE], env: request.env) : nil
|
32
|
+
end
|
31
33
|
|
32
|
-
|
34
|
+
def mapping(path)
|
35
|
+
forward_proxies.keys.find do |key|
|
33
36
|
case key
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
when Regexp
|
38
|
+
key.match(path)
|
39
|
+
else
|
40
|
+
path == key
|
38
41
|
end
|
39
42
|
end
|
40
|
-
|
41
|
-
forward_proxy_builder = forward_proxies[key]
|
42
|
-
forward_proxy_builder ? forward_proxy_builder.resolve(id: request.cookies[RECORDED_ROUTES_COOKIE], env: request.env) : nil
|
43
43
|
end
|
44
44
|
|
45
45
|
def forward_proxies
|
46
46
|
@forward_proxies ||= {}
|
47
47
|
end
|
48
|
-
|
49
48
|
end
|
50
49
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# rubocop:disable Metrics/ParameterLists
|
1
2
|
require 'sitehub/http_headers'
|
2
3
|
require 'sitehub/request_mapping'
|
3
4
|
require 'sitehub/rules'
|
@@ -6,7 +7,6 @@ require 'faraday'
|
|
6
7
|
require 'sitehub/constants'
|
7
8
|
class SiteHub
|
8
9
|
class ForwardProxy
|
9
|
-
|
10
10
|
ERROR_RESPONSE = Rack::Response.new(['error'], 500, {})
|
11
11
|
|
12
12
|
include HttpHeaders, Rules, Resolver, Constants
|
@@ -14,39 +14,48 @@ class SiteHub
|
|
14
14
|
attr_reader :url, :id, :mapped_path, :http_client, :sitehub_cookie_path, :sitehub_cookie_name
|
15
15
|
|
16
16
|
def initialize(url:, id:, mapped_path: nil, rule: nil, sitehub_cookie_path: nil, sitehub_cookie_name:)
|
17
|
-
@id
|
17
|
+
@id = id
|
18
|
+
@url = url
|
19
|
+
@rule = rule
|
20
|
+
@mapped_path = mapped_path
|
18
21
|
@sitehub_cookie_path = sitehub_cookie_path
|
19
22
|
@sitehub_cookie_name = sitehub_cookie_name
|
20
|
-
@http_client = Faraday.new(ssl: {verify:false}) do |con|
|
23
|
+
@http_client = Faraday.new(ssl: { verify: false }) do |con|
|
21
24
|
con.adapter :em_synchrony
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
25
|
-
def call
|
28
|
+
def call(env)
|
26
29
|
source_request = Rack::Request.new(env)
|
30
|
+
request_mapping = env[REQUEST_MAPPING] = request_mapping(source_request)
|
31
|
+
mapped_uri = URI(request_mapping.computed_uri)
|
27
32
|
|
28
|
-
|
33
|
+
downstream_response = proxy_call(request_headers(mapped_uri, source_request), mapped_uri, source_request)
|
29
34
|
|
30
|
-
|
35
|
+
response(downstream_response, source_request)
|
36
|
+
rescue StandardError => e
|
37
|
+
env[ERRORS] << e.message
|
38
|
+
ERROR_RESPONSE.dup
|
39
|
+
end
|
31
40
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
def response(response, source_request)
|
42
|
+
Rack::Response.new(response.body, response.status, sanitise_headers(response.headers)).tap do |r|
|
43
|
+
r.set_cookie(sitehub_cookie_name, path: (sitehub_cookie_path || source_request.path), value: id)
|
44
|
+
end
|
45
|
+
end
|
36
46
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
47
|
+
def request_headers(mapped_uri, source_request)
|
48
|
+
headers = sanitise_headers(extract_http_headers(source_request.env))
|
49
|
+
headers[HOST_HEADER] = "#{mapped_uri.host}:#{mapped_uri.port}"
|
50
|
+
headers[X_FORWARDED_HOST_HEADER] = append_host(headers[X_FORWARDED_HOST_HEADER].to_s, source_request.url)
|
51
|
+
headers
|
52
|
+
end
|
43
53
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
ERROR_RESPONSE.dup
|
54
|
+
def proxy_call(headers, mapped_uri, source_request)
|
55
|
+
http_client.send(source_request.request_method.downcase, mapped_uri) do |request|
|
56
|
+
request.headers = headers
|
57
|
+
request.body = source_request.body.read
|
58
|
+
request.params = source_request.params
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
@@ -54,14 +63,19 @@ class SiteHub
|
|
54
63
|
RequestMapping.new(source_url: source_request.url, mapped_url: url, mapped_path: mapped_path)
|
55
64
|
end
|
56
65
|
|
57
|
-
def ==
|
66
|
+
def ==(other)
|
58
67
|
other.is_a?(ForwardProxy) && url == other.url
|
59
68
|
end
|
60
69
|
|
61
70
|
private
|
71
|
+
|
62
72
|
def append_host(forwarded_host, destination_uri)
|
63
73
|
destination_uri = URI(destination_uri)
|
64
|
-
forwarded_host == EMPTY_STRING
|
74
|
+
if forwarded_host == EMPTY_STRING
|
75
|
+
"#{destination_uri.host}:#{destination_uri.port}"
|
76
|
+
else
|
77
|
+
"#{forwarded_host},#{destination_uri.host}"
|
78
|
+
end
|
65
79
|
end
|
66
80
|
end
|
67
81
|
end
|
@@ -6,17 +6,20 @@ require_relative 'collection/route_collection'
|
|
6
6
|
require_relative 'middleware'
|
7
7
|
|
8
8
|
class SiteHub
|
9
|
-
|
10
9
|
class ForwardProxyBuilder
|
11
10
|
include Middleware
|
12
11
|
include Rules, Resolver
|
13
12
|
|
14
|
-
class InvalidDefinitionException < Exception
|
13
|
+
class InvalidDefinitionException < Exception
|
15
14
|
end
|
16
15
|
|
16
|
+
INVALID_SPLIT_MSG = 'label and url must be defined if not supplying a block'.freeze
|
17
|
+
ROUTES_WITH_SPLITS_MSG = 'you cant register routes and splits at the same level'.freeze
|
18
|
+
INVALID_ROUTE_DEF_MSG = 'rule must be specified when supplying a block'.freeze
|
19
|
+
|
17
20
|
attr_reader :mapped_path, :default_proxy, :routes, :middlewares, :splits, :sitehub_cookie_name
|
18
21
|
|
19
|
-
def initialize(url: nil, mapped_path:, rule:nil, sitehub_cookie_name: nil, &block)
|
22
|
+
def initialize(url: nil, mapped_path:, rule: nil, sitehub_cookie_name: nil, &block)
|
20
23
|
@mapped_path = mapped_path
|
21
24
|
@middlewares = []
|
22
25
|
@splits = Collection::SplitRouteCollection.new
|
@@ -24,10 +27,11 @@ class SiteHub
|
|
24
27
|
@sitehub_cookie_name = sitehub_cookie_name
|
25
28
|
rule(rule) if rule
|
26
29
|
default(url: url) if url
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
30
|
+
|
31
|
+
return unless block
|
32
|
+
|
33
|
+
instance_eval(&block)
|
34
|
+
raise InvalidDefinitionException unless valid?
|
31
35
|
end
|
32
36
|
|
33
37
|
def valid?
|
@@ -35,23 +39,34 @@ class SiteHub
|
|
35
39
|
endpoints.valid?
|
36
40
|
end
|
37
41
|
|
38
|
-
def endpoints
|
42
|
+
def endpoints(collection = nil)
|
39
43
|
return @endpoints || Collection::RouteCollection.new unless collection
|
40
44
|
|
41
|
-
raise InvalidDefinitionException,
|
45
|
+
raise InvalidDefinitionException, ROUTES_WITH_SPLITS_MSG if @endpoints && @endpoints != collection
|
42
46
|
@endpoints = collection
|
43
47
|
end
|
44
48
|
|
45
|
-
def split(percentage:, url
|
49
|
+
def split(percentage:, url: nil, label: nil, &block)
|
46
50
|
endpoints(splits)
|
47
|
-
|
51
|
+
|
52
|
+
raise InvalidDefinitionException, INVALID_SPLIT_MSG unless block || [url, label].all?
|
53
|
+
|
54
|
+
label = label ? label.to_sym : UUID.generate(:compact)
|
55
|
+
|
56
|
+
proxy = block ? new(&block).build : forward_proxy(label: label, url: url)
|
57
|
+
|
58
|
+
endpoints.add label, proxy, percentage
|
59
|
+
end
|
60
|
+
|
61
|
+
def new(&block)
|
62
|
+
self.class.new(mapped_path: mapped_path, &block)
|
48
63
|
end
|
49
64
|
|
50
|
-
def route
|
65
|
+
def route(url: nil, label: nil, rule: nil, &block)
|
51
66
|
endpoints(routes)
|
52
67
|
|
53
68
|
if block
|
54
|
-
raise InvalidDefinitionException,
|
69
|
+
raise InvalidDefinitionException, INVALID_ROUTE_DEF_MSG unless rule
|
55
70
|
builder = self.class.new(mapped_path: mapped_path, rule: rule, &block).build
|
56
71
|
endpoints.add UUID.generate(:compact), builder
|
57
72
|
else
|
@@ -63,12 +78,11 @@ class SiteHub
|
|
63
78
|
@default_proxy = forward_proxy(label: :default, url: url)
|
64
79
|
end
|
65
80
|
|
66
|
-
def sitehub_cookie_path
|
81
|
+
def sitehub_cookie_path(path = nil)
|
67
82
|
return @sitehub_cookie_path unless path
|
68
83
|
@sitehub_cookie_path = path
|
69
84
|
end
|
70
85
|
|
71
|
-
|
72
86
|
def build
|
73
87
|
endpoints.transform do |proxy|
|
74
88
|
apply_middleware(proxy).tap do |wrapped_proxy|
|
@@ -86,14 +100,17 @@ class SiteHub
|
|
86
100
|
endpoints[id] || endpoints.resolve(env: env) || default_proxy
|
87
101
|
end
|
88
102
|
|
89
|
-
def ==
|
90
|
-
other.is_a?(ForwardProxyBuilder) &&
|
103
|
+
def ==(other)
|
104
|
+
other.is_a?(ForwardProxyBuilder) && default_proxy == other.default_proxy && endpoints == other.endpoints
|
91
105
|
end
|
92
106
|
|
93
|
-
|
94
107
|
def forward_proxy(label:, url:, rule: nil)
|
95
|
-
ForwardProxy.new(url: url,
|
108
|
+
ForwardProxy.new(url: url,
|
109
|
+
id: label.to_sym,
|
110
|
+
mapped_path: mapped_path,
|
111
|
+
sitehub_cookie_path: sitehub_cookie_path,
|
112
|
+
sitehub_cookie_name: sitehub_cookie_name,
|
113
|
+
rule: rule)
|
96
114
|
end
|
97
115
|
end
|
98
|
-
|
99
|
-
end
|
116
|
+
end
|
data/lib/sitehub/http_headers.rb
CHANGED
@@ -9,52 +9,65 @@ class SiteHub
|
|
9
9
|
RACK_HTTP_HEADER_ID = /#{HTTP_PREFIX.source}[A-Z_]+$/
|
10
10
|
COMMAND_FOLLOWED_BY_SPACES = /,\s+/
|
11
11
|
|
12
|
+
SHOULD_NOT_TRANSFER = [PROXY_CONNECTION].freeze
|
13
|
+
|
14
|
+
HOP_BY_HOP = [CONNECTION_HEADER,
|
15
|
+
KEEP_ALIVE,
|
16
|
+
PROXY_AUTHENTICATE,
|
17
|
+
PROXY_AUTHORIZATION,
|
18
|
+
TE,
|
19
|
+
TRAILERS,
|
20
|
+
TRANSFER_ENCODING,
|
21
|
+
CONTENT_ENCODING].freeze
|
22
|
+
|
12
23
|
def split_field(f)
|
13
|
-
f ? f.split(COMMAND_FOLLOWED_BY_SPACES).collect
|
24
|
+
f ? f.split(COMMAND_FOLLOWED_BY_SPACES).collect(&:downcase) : []
|
14
25
|
end
|
15
26
|
|
16
27
|
def sanitise_headers(src)
|
17
|
-
|
18
|
-
sanitised_headers = {}
|
19
|
-
|
20
28
|
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
29
|
|
31
|
-
|
30
|
+
{}.tap do |sanitised_headers|
|
31
|
+
src.each do |key, value|
|
32
|
+
key = key.downcase.gsub(UNDERSCORE, HYPHEN)
|
33
|
+
next if HOP_BY_HOP.member?(key) || connections.member?(key) || SHOULD_NOT_TRANSFER.member?(key)
|
34
|
+
sanitised_headers[key] = value
|
35
|
+
end
|
32
36
|
|
33
|
-
|
37
|
+
sanitised_headers[LOCATION_HEADER].gsub!(HTTP_OR_SSL_PORT, EMPTY_STRING) if sanitised_headers[LOCATION_HEADER]
|
38
|
+
end
|
34
39
|
end
|
35
40
|
|
36
|
-
|
37
|
-
|
38
41
|
def extract_http_headers(env)
|
39
|
-
headers = env
|
40
|
-
|
41
|
-
|
42
|
-
[reconstruct_header_name(k), v]
|
43
|
-
end.inject(Rack::Utils::HeaderHash.new) do |hash, k_v|
|
42
|
+
headers = remove_excluded_headers(env)
|
43
|
+
|
44
|
+
headers = headers.to_a.each_with_object(Rack::Utils::HeaderHash.new) do |k_v, hash|
|
44
45
|
k, v = k_v
|
45
|
-
hash[k] = v
|
46
|
+
hash[reconstruct_header_name(k)] = v
|
46
47
|
hash
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
+
remote_address = env[RackHttpHeaderKeys::REMOTE_ADDRESS_ENV_KEY]
|
51
|
+
headers.merge!(X_FORWARDED_FOR_HEADER => x_forwarded_for_value(headers, remote_address))
|
52
|
+
end
|
50
53
|
|
51
|
-
|
54
|
+
def x_forwarded_for_value(headers, remote_address)
|
55
|
+
(forwarded_address_list(headers) << remote_address).join(COMMA_WITH_SPACE)
|
52
56
|
end
|
53
57
|
|
58
|
+
def forwarded_address_list(headers)
|
59
|
+
headers[X_FORWARDED_FOR_HEADER].to_s.split(COMMAND_FOLLOWED_BY_SPACES)
|
60
|
+
end
|
54
61
|
|
62
|
+
def remove_excluded_headers(env)
|
63
|
+
env.reject do |k, v|
|
64
|
+
!RackHttpHeaderKeys::HTTP_HEADER_FILTER_EXCEPTIONS.include?(k.to_s.upcase) &&
|
65
|
+
(!RACK_HTTP_HEADER_ID.match(k) || v.nil?)
|
66
|
+
end
|
67
|
+
end
|
55
68
|
|
56
69
|
def reconstruct_header_name(name)
|
57
70
|
name.sub(HTTP_PREFIX, EMPTY_STRING).gsub(UNDERSCORE, HYPHEN)
|
58
71
|
end
|
59
72
|
end
|
60
|
-
end
|
73
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
1
2
|
require 'logger'
|
2
3
|
require 'rack/commonlogger'
|
3
4
|
require_relative 'log_wrapper'
|
4
5
|
require 'sitehub/constants'
|
5
6
|
|
6
|
-
#Very heavily based on Rack::CommonLogger
|
7
|
+
# Very heavily based on Rack::CommonLogger
|
7
8
|
class SiteHub
|
8
9
|
module Logging
|
9
10
|
class AccessLogger
|
@@ -11,54 +12,57 @@ class SiteHub
|
|
11
12
|
|
12
13
|
include Constants
|
13
14
|
|
14
|
-
FORMAT = %
|
15
|
-
|
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'
|
15
|
+
FORMAT = %(%s - %s [%s] transaction_id:%s: "%s %s%s => %s %s" %d %s %0.4f\n).freeze
|
16
|
+
ZERO_STRING = '0'.freeze
|
26
17
|
STATUS_RANGE = 0..3
|
27
18
|
|
28
|
-
def initialize
|
19
|
+
def initialize(app, logger = ::Logger.new(STDOUT))
|
29
20
|
@app = app
|
30
21
|
@logger = LogWrapper.new(logger)
|
31
22
|
end
|
32
23
|
|
33
|
-
def call
|
24
|
+
def call(env)
|
34
25
|
start_time = Time.now
|
35
26
|
|
36
27
|
@app.call(env).tap do |response|
|
37
|
-
status, headers,
|
38
|
-
|
28
|
+
status, headers, _body = response.to_a
|
29
|
+
log_message = format(log_template, *log_content(start_time, env, headers, env[REQUEST_MAPPING], status))
|
30
|
+
logger.write(log_message)
|
39
31
|
end
|
40
32
|
end
|
41
33
|
|
42
|
-
def
|
34
|
+
def log_content(began_at, env, header, mapped_request, status)
|
43
35
|
now = Time.now
|
44
|
-
|
36
|
+
[
|
37
|
+
source_address(env),
|
38
|
+
remote_user(env[RackHttpHeaderKeys::REMOTE_USER]),
|
39
|
+
now.strftime(TIME_STAMP_FORMAT),
|
40
|
+
env[RackHttpHeaderKeys::TRANSACTION_ID],
|
41
|
+
env[RackHttpHeaderKeys::REQUEST_METHOD],
|
42
|
+
env[RackHttpHeaderKeys::PATH_INFO],
|
43
|
+
query_string(env[RackHttpHeaderKeys::QUERY_STRING]),
|
44
|
+
mapped_url(mapped_request),
|
45
|
+
env[RackHttpHeaderKeys::HTTP_VERSION],
|
46
|
+
status.to_s[STATUS_RANGE],
|
47
|
+
extract_content_length(header),
|
48
|
+
now - began_at
|
49
|
+
]
|
50
|
+
end
|
45
51
|
|
52
|
+
def mapped_url(mapped_request)
|
53
|
+
mapped_request ? mapped_request.mapped_url.to_s : EMPTY_STRING
|
54
|
+
end
|
46
55
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
env[QUERY_STRING].empty? ? EMPTY_STRING : QUESTION_MARK+env[QUERY_STRING],
|
55
|
-
mapped_request ? mapped_request.mapped_url.to_s : EMPTY_STRING,
|
56
|
-
env[HTTP_VERSION],
|
57
|
-
status.to_s[STATUS_RANGE],
|
58
|
-
length,
|
59
|
-
now - began_at]
|
56
|
+
def query_string(query_string)
|
57
|
+
query_string.empty? ? EMPTY_STRING : QUESTION_MARK + query_string
|
58
|
+
end
|
59
|
+
|
60
|
+
def remote_user(remote_user)
|
61
|
+
remote_user || '-'
|
62
|
+
end
|
60
63
|
|
61
|
-
|
64
|
+
def source_address(env)
|
65
|
+
env[RackHttpHeaderKeys::X_FORWARDED_FOR] || env[RackHttpHeaderKeys::REMOTE_ADDR] || HYPHEN
|
62
66
|
end
|
63
67
|
|
64
68
|
def log_template
|
@@ -66,9 +70,9 @@ class SiteHub
|
|
66
70
|
end
|
67
71
|
|
68
72
|
def extract_content_length(headers)
|
69
|
-
value = headers[CONTENT_LENGTH]
|
73
|
+
(value = headers[HttpHeaderKeys::CONTENT_LENGTH]) || (return HYPHEN)
|
70
74
|
value.to_s == ZERO_STRING ? HYPHEN : value
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
74
|
-
end
|
78
|
+
end
|
@@ -6,8 +6,7 @@ class SiteHub
|
|
6
6
|
module Logging
|
7
7
|
class ErrorLogger
|
8
8
|
include Constants
|
9
|
-
LOG_TEMPLATE = '[%s] ERROR: %s - %s'
|
10
|
-
|
9
|
+
LOG_TEMPLATE = '[%s] ERROR: %s - %s'.freeze
|
11
10
|
|
12
11
|
attr_reader :logger
|
13
12
|
|
@@ -16,11 +15,13 @@ class SiteHub
|
|
16
15
|
@logger = LogWrapper.new(logger)
|
17
16
|
end
|
18
17
|
|
19
|
-
def call
|
18
|
+
def call(env)
|
20
19
|
env[ERRORS] ||= LogStash.new
|
21
20
|
@app.call(env).tap do
|
22
21
|
unless env[ERRORS].empty?
|
23
|
-
messages = env[ERRORS].collect
|
22
|
+
messages = env[ERRORS].collect do |log_entry|
|
23
|
+
log_message(error: log_entry.message, transaction_id: env[RackHttpHeaderKeys::TRANSACTION_ID])
|
24
|
+
end
|
24
25
|
|
25
26
|
logger.write(messages.join(NEW_LINE))
|
26
27
|
end
|
@@ -28,9 +29,8 @@ class SiteHub
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def log_message(error:, transaction_id:)
|
31
|
-
LOG_TEMPLATE
|
32
|
+
format(LOG_TEMPLATE, Time.now.strftime(TIME_STAMP_FORMAT), transaction_id, error)
|
32
33
|
end
|
33
|
-
|
34
34
|
end
|
35
35
|
end
|
36
|
-
end
|
36
|
+
end
|
@@ -2,13 +2,14 @@ class SiteHub
|
|
2
2
|
module Logging
|
3
3
|
class LogEntry
|
4
4
|
attr_reader :message, :time
|
5
|
-
def initialize
|
6
|
-
@message
|
5
|
+
def initialize(message, time = Time.now)
|
6
|
+
@message = message
|
7
|
+
@time = time
|
7
8
|
end
|
8
9
|
|
9
|
-
def ==
|
10
|
-
other.is_a?(LogEntry) &&
|
10
|
+
def ==(other)
|
11
|
+
other.is_a?(LogEntry) && message == other.message && time == other.time
|
11
12
|
end
|
12
13
|
end
|
13
14
|
end
|
14
|
-
end
|
15
|
+
end
|
@@ -3,11 +3,11 @@ class SiteHub
|
|
3
3
|
class LogWrapper
|
4
4
|
attr_reader :logger
|
5
5
|
|
6
|
-
def initialize
|
6
|
+
def initialize(logger)
|
7
7
|
@logger = logger
|
8
8
|
end
|
9
9
|
|
10
|
-
def write
|
10
|
+
def write(msg)
|
11
11
|
if logger.respond_to?(:<<)
|
12
12
|
logger << msg
|
13
13
|
elsif logger.respond_to?(:write)
|
@@ -15,9 +15,9 @@ class SiteHub
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def ==
|
19
|
-
other.is_a?(LogWrapper) &&
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(LogWrapper) && logger == other.logger
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
-
end
|
23
|
+
end
|
data/lib/sitehub/logging.rb
CHANGED
data/lib/sitehub/middleware.rb
CHANGED
@@ -5,7 +5,7 @@ class SiteHub
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def use(middleware_clazz, *args, &block)
|
8
|
-
middlewares << [middleware_clazz, args, block|| proc{}]
|
8
|
+
middlewares << [middleware_clazz, args, block || proc {}]
|
9
9
|
end
|
10
10
|
|
11
11
|
def apply_middleware(forward_proxy)
|
@@ -18,4 +18,4 @@ class SiteHub
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
@@ -2,31 +2,31 @@ class SiteHub
|
|
2
2
|
class PathDirective
|
3
3
|
attr_reader :matcher, :path_template
|
4
4
|
|
5
|
-
def initialize
|
6
|
-
@matcher
|
5
|
+
def initialize(matcher, path_template)
|
6
|
+
@matcher = matcher
|
7
|
+
@path_template = path_template
|
7
8
|
end
|
8
9
|
|
9
|
-
def match?
|
10
|
-
|
10
|
+
def match?(url)
|
11
|
+
!matcher.match(url).nil?
|
11
12
|
end
|
12
13
|
|
13
14
|
def path_template
|
14
15
|
@path_template.dup
|
15
16
|
end
|
16
17
|
|
17
|
-
def apply
|
18
|
+
def apply(url)
|
18
19
|
url_components = matcher.match(url).captures
|
19
20
|
|
20
21
|
path_template.tap do |p|
|
21
22
|
url_components.each_with_index do |m, index|
|
22
|
-
p.gsub!(RequestMapping::CAPTURE_GROUP_REFERENCE % (index+1), m)
|
23
|
+
p.gsub!(RequestMapping::CAPTURE_GROUP_REFERENCE % (index + 1), m)
|
23
24
|
end
|
24
25
|
end
|
25
|
-
|
26
26
|
end
|
27
27
|
|
28
|
-
def ==
|
29
|
-
other.is_a?(PathDirective) &&
|
28
|
+
def ==(other)
|
29
|
+
other.is_a?(PathDirective) && matcher == other.matcher && path_template == other.path_template
|
30
30
|
end
|
31
31
|
end
|
32
|
-
end
|
32
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'sitehub/path_directive'
|
2
2
|
class SiteHub
|
3
3
|
class PathDirectives < Array
|
4
|
-
def initialize
|
4
|
+
def initialize(map = {})
|
5
5
|
enriched = map.map do |array|
|
6
|
-
matcher
|
6
|
+
matcher = array.first
|
7
|
+
path_template = array.last
|
7
8
|
|
8
|
-
matcher = matcher.is_a?(Regexp) ? matcher :
|
9
|
+
matcher = matcher.is_a?(Regexp) ? matcher : /#{matcher}/
|
9
10
|
PathDirective.new(matcher, path_template)
|
10
11
|
end
|
11
12
|
|
@@ -18,4 +19,4 @@ class SiteHub
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
end
|
21
|
-
end
|
22
|
+
end
|