sitehub 0.4.3 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +31 -0
  3. data/.gitignore +2 -1
  4. data/.reek +41 -0
  5. data/.simplecov +7 -0
  6. data/Gemfile.lock +61 -33
  7. data/README.md +4 -0
  8. data/Rakefile +1 -1
  9. data/circle.yml +1 -1
  10. data/lib/sitehub/builder.rb +19 -36
  11. data/lib/sitehub/collection/split_route_collection.rb +18 -13
  12. data/lib/sitehub/collection/split_route_collection/split.rb +6 -4
  13. data/lib/sitehub/constants.rb +2 -1
  14. data/lib/sitehub/constants/http_header_keys.rb +2 -0
  15. data/lib/sitehub/constants/rack_http_header_keys.rb +2 -0
  16. data/lib/sitehub/cookie.rb +4 -13
  17. data/lib/sitehub/cookie/attribute.rb +10 -9
  18. data/lib/sitehub/cookie/flag.rb +5 -8
  19. data/lib/sitehub/cookie_rewriting.rb +12 -5
  20. data/lib/sitehub/downstream_client.rb +37 -0
  21. data/lib/sitehub/equality.rb +28 -0
  22. data/lib/sitehub/forward_proxy.rb +19 -62
  23. data/lib/sitehub/forward_proxy_builder.rb +70 -49
  24. data/lib/sitehub/getter_setter_methods.rb +21 -0
  25. data/lib/sitehub/http_headers.rb +45 -48
  26. data/lib/sitehub/location_rewriter.rb +29 -0
  27. data/lib/sitehub/location_rewriters.rb +23 -0
  28. data/lib/sitehub/memoize.rb +25 -0
  29. data/lib/sitehub/middleware.rb +16 -6
  30. data/lib/sitehub/middleware/error_handling.rb +20 -0
  31. data/lib/sitehub/middleware/forward_proxies.rb +54 -0
  32. data/lib/sitehub/{logging.rb → middleware/logging.rb} +0 -0
  33. data/lib/sitehub/middleware/logging/access_logger.rb +36 -0
  34. data/lib/sitehub/middleware/logging/error_logger.rb +38 -0
  35. data/lib/sitehub/middleware/logging/log_entry.rb +16 -0
  36. data/lib/sitehub/middleware/logging/log_stash.rb +12 -0
  37. data/lib/sitehub/middleware/logging/log_wrapper.rb +24 -0
  38. data/lib/sitehub/middleware/logging/request_log.rb +74 -0
  39. data/lib/sitehub/middleware/reverse_proxy.rb +37 -0
  40. data/lib/sitehub/middleware/transaction_id.rb +18 -0
  41. data/lib/sitehub/nil_location_rewriter.rb +7 -0
  42. data/lib/sitehub/nil_proxy.rb +11 -0
  43. data/lib/sitehub/request.rb +101 -0
  44. data/lib/sitehub/request_mapping.rb +16 -18
  45. data/lib/sitehub/resolver.rb +1 -1
  46. data/lib/sitehub/response.rb +10 -0
  47. data/lib/sitehub/string_utils.rb +13 -0
  48. data/lib/sitehub/version.rb +1 -1
  49. data/sitehub.gemspec +4 -1
  50. data/spec/equality_spec.rb +32 -0
  51. data/spec/sitehub/builder_spec.rb +29 -22
  52. data/spec/sitehub/collection/route_collection_spec.rb +15 -14
  53. data/spec/sitehub/collection/split_route_collection/split_spec.rb +26 -0
  54. data/spec/sitehub/collection/split_route_collection_spec.rb +15 -3
  55. data/spec/sitehub/cookie/flag_spec.rb +1 -1
  56. data/spec/sitehub/cookie_rewriting_spec.rb +6 -10
  57. data/spec/sitehub/downstream_client_spec.rb +72 -0
  58. data/spec/sitehub/equality_spec.rb +32 -0
  59. data/spec/sitehub/forward_proxy_builder_spec.rb +92 -55
  60. data/spec/sitehub/forward_proxy_spec.rb +29 -97
  61. data/spec/sitehub/http_headers_spec.rb +32 -52
  62. data/spec/sitehub/integration_spec.rb +1 -1
  63. data/spec/sitehub/location_rewriter_spec.rb +46 -0
  64. data/spec/sitehub/{path_directives_spec.rb → location_rewriters_spec.rb} +8 -8
  65. data/spec/sitehub/memoize_spec.rb +56 -0
  66. data/spec/sitehub/middleware/error_handling_spec.rb +34 -0
  67. data/spec/sitehub/middleware/forward_proxies_spec.rb +105 -0
  68. data/spec/sitehub/middleware/logging/access_logger_spec.rb +51 -0
  69. data/spec/sitehub/middleware/logging/error_logger_spec.rb +84 -0
  70. data/spec/sitehub/middleware/logging/log_entry_spec.rb +33 -0
  71. data/spec/sitehub/middleware/logging/log_stash_spec.rb +21 -0
  72. data/spec/sitehub/middleware/logging/log_wrapper_spec.rb +31 -0
  73. data/spec/sitehub/middleware/logging/request_log_spec.rb +108 -0
  74. data/spec/sitehub/middleware/reverse_proxy_spec.rb +113 -0
  75. data/spec/sitehub/middleware/transaction_id_spec.rb +30 -0
  76. data/spec/sitehub/middleware_spec.rb +23 -13
  77. data/spec/sitehub/nil_location_rewriter_spec.rb +10 -0
  78. data/spec/sitehub/nil_proxy_spec.rb +14 -0
  79. data/spec/sitehub/request_mapping_spec.rb +21 -23
  80. data/spec/sitehub/request_spec.rb +228 -0
  81. data/spec/sitehub/resolver_spec.rb +2 -5
  82. data/spec/sitehub/response_spec.rb +30 -0
  83. data/spec/spec_helper.rb +12 -6
  84. data/spec/support/async/middleware.rb +1 -0
  85. data/spec/support/patch/rack/response.rb +7 -5
  86. data/spec/support/shared_contexts.rb +3 -0
  87. data/spec/support/shared_contexts/http_proxy_rules_context.rb +36 -0
  88. data/spec/support/shared_contexts/middleware_context.rb +6 -6
  89. data/spec/support/shared_contexts/module_spec_context.rb +7 -0
  90. data/spec/support/shared_contexts/rack_request_context.rb +18 -0
  91. data/spec/support/shared_contexts/rack_test_context.rb +0 -1
  92. data/spec/support/shared_examples.rb +3 -0
  93. data/spec/support/shared_examples/memoized_helpers.rb +7 -0
  94. data/spec/support/shared_examples/prohibited_http_header_filter.rb +16 -0
  95. data/spec/support/silent_warnings.rb +1 -1
  96. data/tasks/code_quality.rake +6 -0
  97. metadata +99 -29
  98. data/lib/sitehub/forward_proxies.rb +0 -49
  99. data/lib/sitehub/logging/access_logger.rb +0 -78
  100. data/lib/sitehub/logging/error_logger.rb +0 -36
  101. data/lib/sitehub/logging/log_entry.rb +0 -15
  102. data/lib/sitehub/logging/log_stash.rb +0 -10
  103. data/lib/sitehub/logging/log_wrapper.rb +0 -23
  104. data/lib/sitehub/path_directive.rb +0 -32
  105. data/lib/sitehub/path_directives.rb +0 -22
  106. data/lib/sitehub/reverse_proxy.rb +0 -57
  107. data/lib/sitehub/string_sanitiser.rb +0 -7
  108. data/lib/sitehub/transaction_id.rb +0 -16
  109. data/spec/sitehub/error_handling_spec.rb +0 -20
  110. data/spec/sitehub/forward_proxies_spec.rb +0 -103
  111. data/spec/sitehub/logging/access_logger_spec.rb +0 -128
  112. data/spec/sitehub/logging/error_logger_spec.rb +0 -78
  113. data/spec/sitehub/logging/log_entry_spec.rb +0 -31
  114. data/spec/sitehub/logging/log_stash_spec.rb +0 -19
  115. data/spec/sitehub/logging/log_wrapper_spec.rb +0 -29
  116. data/spec/sitehub/path_directive_spec.rb +0 -47
  117. data/spec/sitehub/reverse_proxy_spec.rb +0 -111
  118. data/spec/sitehub/transaction_id_spec.rb +0 -28
  119. data/spec/support/async/response_handler.rb +0 -16
  120. data/spec/support/shared_contexts/async_context.rb +0 -14
@@ -0,0 +1,21 @@
1
+ class SiteHub
2
+ module GetterSetterMethods
3
+ def getter_setters(*method_names)
4
+ method_names.each do |method_name|
5
+ getter_setter method_name.to_sym
6
+ end
7
+ end
8
+
9
+ def getter_setter(method_name, default = nil)
10
+ define_method method_name do |arg = nil|
11
+ attribute_name = "@#{method_name}"
12
+ if arg
13
+ instance_variable_set(attribute_name, arg)
14
+ self
15
+ else
16
+ instance_variable_get(attribute_name) || default
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,73 +1,70 @@
1
- require 'sitehub/constants'
2
1
  class SiteHub
3
- module HttpHeaders
4
- include Constants::HttpHeaderKeys
5
- include Constants
6
-
7
- HTTP_OR_SSL_PORT = /:80(?!\d+)|:443/
2
+ class HttpHeaders < Hash
8
3
  HTTP_PREFIX = /^HTTP_/
9
4
  RACK_HTTP_HEADER_ID = /#{HTTP_PREFIX.source}[A-Z_]+$/
10
- COMMAND_FOLLOWED_BY_SPACES = /,\s+/
11
-
12
- SHOULD_NOT_TRANSFER = [PROXY_CONNECTION].freeze
13
5
 
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
6
+ class << self
7
+ def from_rack_env(env)
8
+ new(format_keys(remove_rack_specific_headers(env.dup)))
9
+ end
22
10
 
23
- def split_field(f)
24
- f ? f.split(COMMAND_FOLLOWED_BY_SPACES).collect(&:downcase) : []
25
- end
11
+ private
26
12
 
27
- def sanitise_headers(src)
28
- connections = split_field(src[CONNECTION_HEADER])
13
+ def remove_rack_specific_headers(env)
14
+ env.reject do |key, value|
15
+ !Constants::RackHttpHeaderKeys::HTTP_HEADER_FILTER_EXCEPTIONS.include?(key.to_s.upcase) &&
16
+ (!RACK_HTTP_HEADER_ID.match(key) || !value)
17
+ end
18
+ end
29
19
 
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
20
+ def format_keys(env)
21
+ env.each_with_object({}) do |key_value, hash|
22
+ key, value = *key_value
23
+ hash[header_name(key)] = value
35
24
  end
25
+ end
36
26
 
37
- sanitised_headers[LOCATION_HEADER].gsub!(HTTP_OR_SSL_PORT, EMPTY_STRING) if sanitised_headers[LOCATION_HEADER]
27
+ def header_name(name)
28
+ name.sub(HTTP_PREFIX, EMPTY_STRING).downcase.gsub(UNDERSCORE, HYPHEN)
38
29
  end
39
30
  end
40
31
 
41
- def extract_http_headers(env)
42
- headers = remove_excluded_headers(env)
32
+ include Constants, Constants::HttpHeaderKeys
33
+
34
+ EXCLUDED_HEADERS = [CONNECTION_HEADER,
35
+ KEEP_ALIVE,
36
+ PROXY_CONNECTION,
37
+ PROXY_AUTHENTICATE,
38
+ PROXY_AUTHORIZATION,
39
+ TE,
40
+ TRAILERS,
41
+ TRANSFER_ENCODING,
42
+ CONTENT_ENCODING,
43
+ UPGRADE].freeze
43
44
 
44
- headers = headers.to_a.each_with_object(Rack::Utils::HeaderHash.new) do |k_v, hash|
45
- k, v = k_v
46
- hash[reconstruct_header_name(k)] = v
47
- hash
45
+ def initialize(env)
46
+ env.each do |key, value|
47
+ self[key.to_s.downcase] = value
48
48
  end
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))
50
+ filter_prohibited_headers
52
51
  end
53
52
 
54
- def x_forwarded_for_value(headers, remote_address)
55
- (forwarded_address_list(headers) << remote_address).join(COMMA_WITH_SPACE)
56
- end
53
+ private
57
54
 
58
- def forwarded_address_list(headers)
59
- headers[X_FORWARDED_FOR_HEADER].to_s.split(COMMAND_FOLLOWED_BY_SPACES)
55
+ def filter_prohibited_headers
56
+ remove_headers(hop_by_hop_headers.concat(EXCLUDED_HEADERS))
60
57
  end
61
58
 
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
59
+ def hop_by_hop_headers
60
+ field = self[CONNECTION_HEADER] || EMPTY_STRING
61
+ field.split(COMMA).collect(&:downcase)
67
62
  end
68
63
 
69
- def reconstruct_header_name(name)
70
- name.sub(HTTP_PREFIX, EMPTY_STRING).gsub(UNDERSCORE, HYPHEN)
64
+ def remove_headers(excluded)
65
+ reject! do |key, _value|
66
+ excluded.member?(key)
67
+ end
71
68
  end
72
69
  end
73
70
  end
@@ -0,0 +1,29 @@
1
+ require 'sitehub/equality'
2
+ class SiteHub
3
+ class LocationRewriter
4
+ include Equality
5
+
6
+ attr_reader :matcher, :path_template
7
+
8
+ def initialize(matcher, path_template)
9
+ @matcher = matcher
10
+ @path_template = path_template
11
+ end
12
+
13
+ def match?(url)
14
+ matcher.match(url).is_a?(MatchData)
15
+ end
16
+
17
+ def apply(downstream_url, source_url)
18
+ url_components = matcher.match(downstream_url).captures
19
+
20
+ path = path_template.dup.tap do |template|
21
+ url_components.each.with_index(1) do |component, index|
22
+ template.gsub!(RequestMapping::CAPTURE_GROUP_REFERENCE % index, component)
23
+ end
24
+ end
25
+
26
+ "#{source_url[RequestMapping::BASE_URL_MATCHER]}#{path}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'sitehub/location_rewriter'
2
+ require 'sitehub/nil_location_rewriter'
3
+ class SiteHub
4
+ class LocationRewriters < Array
5
+ DEFAULT = NilLocationRewriter.new
6
+
7
+ def initialize(map = {})
8
+ enriched = map.collect do |pattern, path_template|
9
+ matcher = pattern.is_a?(Regexp) ? pattern : /#{pattern}/
10
+ LocationRewriter.new(matcher, path_template)
11
+ end
12
+
13
+ super enriched
14
+ end
15
+
16
+ def find(url)
17
+ result = super() do |directive|
18
+ directive.match?(url)
19
+ end
20
+ result || DEFAULT
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ class SiteHub
2
+ module Memoize
3
+ def memoize(*methods)
4
+ methods.each do |method|
5
+ method_alias = "_#{method}"
6
+ alias_method method_alias, method
7
+
8
+ define_memoized_method(method, method_alias)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def define_memoized_method(method, method_alias)
15
+ define_method(method) do |*args, &block|
16
+ attribute = "@#{method}".gsub('?', 'question_mark')
17
+ return instance_variable_get(attribute) if instance_variable_defined? attribute
18
+
19
+ send(method_alias, *args, &block).tap do |result|
20
+ instance_variable_set(attribute, result)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,20 +1,30 @@
1
+ $LOAD_PATH.unshift(__dir__)
2
+ require 'middleware/logging'
3
+ require 'middleware/transaction_id'
4
+ require 'middleware/error_handling'
5
+ require 'middleware/forward_proxies'
6
+ require 'middleware/reverse_proxy'
7
+ require 'rack/ssl-enforcer'
8
+ require 'rack/fiber_pool'
9
+
1
10
  class SiteHub
2
11
  module Middleware
3
12
  def middlewares
4
13
  @middleware ||= []
5
14
  end
6
15
 
16
+ def middleware?
17
+ !middlewares.empty?
18
+ end
19
+
7
20
  def use(middleware_clazz, *args, &block)
8
- middlewares << [middleware_clazz, args, block || proc {}]
21
+ middlewares << [middleware_clazz, args, block]
9
22
  end
10
23
 
11
24
  def apply_middleware(forward_proxy)
12
25
  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)
26
+ middleware, args, block = *middleware_def
27
+ middleware.new(app, *args, &(block || proc {}))
18
28
  end
19
29
  end
20
30
  end
@@ -0,0 +1,20 @@
1
+ class SiteHub
2
+ module Middleware
3
+ class ErrorHandling
4
+ ERROR_RESPONSE = Rack::Response.new(['error'], 500, {})
5
+
6
+ attr_reader :app
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @app.call env
14
+ rescue StandardError => exception
15
+ env[ERRORS] << exception.message
16
+ ERROR_RESPONSE.dup
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ require 'sitehub/constants'
2
+ require 'sitehub/nil_proxy'
3
+ require 'rack/request'
4
+ require 'rack/response'
5
+ require 'rack/utils'
6
+ require 'em-http'
7
+
8
+ class SiteHub
9
+ module Middleware
10
+ class ForwardProxies
11
+ NIL_PROXY = NilProxy.new
12
+
13
+ def call(env)
14
+ source_request = Rack::Request.new(env)
15
+
16
+ forward_proxy = mapped_proxy(path: source_request.path, request: source_request)
17
+
18
+ forward_proxy.call(env)
19
+ end
20
+
21
+ def init
22
+ forward_proxies.values.each(&:build)
23
+ self
24
+ end
25
+
26
+ def <<(route)
27
+ forward_proxies[route.mapped_path] = route
28
+ end
29
+
30
+ def mapped_proxy(path:, request:)
31
+ forward_proxies[mapping(path)].resolve(id: request.cookies[RECORDED_ROUTES_COOKIE], env: request.env)
32
+ end
33
+
34
+ def mapping(path)
35
+ forward_proxies.keys.find do |key|
36
+ case key
37
+ when Regexp
38
+ key.match(path)
39
+ else
40
+ path == key
41
+ end
42
+ end
43
+ end
44
+
45
+ def forward_proxies
46
+ @forward_proxies ||= begin
47
+ {}.tap do |hash|
48
+ hash.default = NIL_PROXY
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
File without changes
@@ -0,0 +1,36 @@
1
+ require 'logger'
2
+ require 'rack/commonlogger'
3
+ require_relative 'log_wrapper'
4
+ require 'sitehub/constants'
5
+ require 'sitehub/middleware/logging/request_log'
6
+ require 'sitehub/response'
7
+
8
+ class SiteHub
9
+ module Middleware
10
+ module Logging
11
+ class AccessLogger
12
+ attr_reader :logger, :start_time
13
+
14
+ include Constants
15
+
16
+ FORMAT = %(%s - %s [%s] transaction_id:%s: "%s %s%s => %s %s" %d %s %0.4f\n).freeze
17
+ ZERO_STRING = '0'.freeze
18
+ STATUS_RANGE = 0..3
19
+
20
+ def initialize(app, logger = ::Logger.new(STDOUT))
21
+ @app = app
22
+ @logger = LogWrapper.new(logger)
23
+ end
24
+
25
+ def call(env)
26
+ request = env[REQUEST] = Request.new(env: env)
27
+ @app.call(env).tap do |response|
28
+ status, headers, body = response.to_a
29
+ response = Response.new(body, status, headers)
30
+ logger.write(RequestLog.new(request, response).to_s)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ require 'logger'
2
+ require 'sitehub/constants'
3
+ require_relative 'log_wrapper'
4
+ require_relative 'log_stash'
5
+ class SiteHub
6
+ module Middleware
7
+ module Logging
8
+ class ErrorLogger
9
+ include Constants
10
+ LOG_TEMPLATE = '[%s] ERROR: %s - %s'.freeze
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
+ errors = env[ERRORS] ||= LogStash.new
21
+ @app.call(env).tap do
22
+ unless errors.empty?
23
+ messages = errors.collect do |log_entry|
24
+ log_message(error: log_entry.message, transaction_id: env[RackHttpHeaderKeys::TRANSACTION_ID])
25
+ end
26
+
27
+ logger.write(messages.join(NEW_LINE))
28
+ end
29
+ end
30
+ end
31
+
32
+ def log_message(error:, transaction_id:)
33
+ format(LOG_TEMPLATE, Time.now.strftime(TIME_STAMP_FORMAT), transaction_id, error)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ require 'sitehub/equality'
2
+ class SiteHub
3
+ module Middleware
4
+ module Logging
5
+ class LogEntry
6
+ include Equality
7
+ attr_reader :message, :time
8
+
9
+ def initialize(message, time = Time.now)
10
+ @message = message
11
+ @time = time
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'log_entry'
2
+ class SiteHub
3
+ module Middleware
4
+ module Logging
5
+ class LogStash < Array
6
+ def <<(message)
7
+ super(LogEntry.new(message))
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end