sitehub 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|