gruf 1.2.7 → 2.0.0

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +98 -119
  4. data/bin/gruf +9 -3
  5. data/lib/gruf.rb +4 -4
  6. data/lib/gruf/configuration.rb +11 -20
  7. data/lib/gruf/controllers/base.rb +82 -0
  8. data/lib/gruf/controllers/request.rb +96 -0
  9. data/lib/gruf/controllers/service_binder.rb +86 -0
  10. data/lib/gruf/error.rb +9 -0
  11. data/lib/gruf/errors/helpers.rb +40 -0
  12. data/lib/gruf/{hooks → interceptors}/active_record/connection_reset.rb +4 -10
  13. data/lib/gruf/interceptors/authentication/basic.rb +80 -0
  14. data/lib/gruf/interceptors/base.rb +51 -0
  15. data/lib/gruf/{instrumentation/output_metadata_timer.rb → interceptors/context.rb} +25 -15
  16. data/lib/gruf/interceptors/instrumentation/output_metadata_timer.rb +59 -0
  17. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/base.rb +15 -13
  18. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/logstash.rb +15 -13
  19. data/lib/gruf/{instrumentation → interceptors/instrumentation}/request_logging/formatters/plain.rb +21 -19
  20. data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +191 -0
  21. data/lib/gruf/interceptors/instrumentation/statsd.rb +80 -0
  22. data/lib/gruf/interceptors/registry.rb +131 -0
  23. data/lib/gruf/{authentication/none.rb → interceptors/server_interceptor.rb} +8 -7
  24. data/lib/gruf/interceptors/timer.rb +79 -0
  25. data/lib/gruf/response.rb +1 -2
  26. data/lib/gruf/server.rb +40 -25
  27. data/lib/gruf/version.rb +1 -1
  28. metadata +19 -20
  29. data/lib/gruf/authentication.rb +0 -65
  30. data/lib/gruf/authentication/base.rb +0 -65
  31. data/lib/gruf/authentication/basic.rb +0 -74
  32. data/lib/gruf/authentication/strategies.rb +0 -107
  33. data/lib/gruf/hooks/base.rb +0 -66
  34. data/lib/gruf/hooks/registry.rb +0 -110
  35. data/lib/gruf/instrumentation/base.rb +0 -114
  36. data/lib/gruf/instrumentation/registry.rb +0 -104
  37. data/lib/gruf/instrumentation/request_context.rb +0 -82
  38. data/lib/gruf/instrumentation/request_logging/hook.rb +0 -185
  39. data/lib/gruf/instrumentation/statsd.rb +0 -80
  40. data/lib/gruf/service.rb +0 -333
@@ -0,0 +1,82 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ require_relative 'request'
18
+ require_relative 'service_binder'
19
+
20
+ module Gruf
21
+ module Controllers
22
+ ##
23
+ # Base controller object for Gruf gRPC requests
24
+ #
25
+ class Base
26
+ include Gruf::Errors::Helpers
27
+
28
+ # @var [Gruf::Controller::Request] request
29
+ attr_reader :request
30
+ # @var [Gruf::Error] error
31
+ attr_reader :error
32
+
33
+ ##
34
+ # Initialize the controller within the given request context
35
+ #
36
+ # @param [Symbol] method_key
37
+ # @param [GRPC::GenericService] service
38
+ # @param [GRPC::RpcDesc] rpc_desc
39
+ # @param [GRPC::ActiveCall] active_call
40
+ # @param [Google::Protobuf::MessageExts] message
41
+ #
42
+ def initialize(method_key:, service:, rpc_desc:, active_call:, message:)
43
+ @request = Request.new(
44
+ method_key: method_key,
45
+ service: service,
46
+ rpc_desc: rpc_desc,
47
+ active_call: active_call,
48
+ message: message
49
+ )
50
+ @error = Gruf::Error.new
51
+ @interceptors = Gruf.interceptors.prepare(@request, @error)
52
+ end
53
+
54
+ ##
55
+ # Bind the controller to the given service and add it to the service registry
56
+ #
57
+ # @param [GRPC::GenericService] service
58
+ #
59
+ def self.bind(service)
60
+ Gruf.services << service.name.constantize
61
+ ServiceBinder.new(service).bind!(self)
62
+ end
63
+
64
+ ##
65
+ # Call a method on this controller
66
+ #
67
+ # @param [Symbol] method_key
68
+ #
69
+ def call(method_key, &block)
70
+ Interceptors::Context.new(@interceptors).intercept! do
71
+ send(method_key, &block)
72
+ end
73
+ rescue GRPC::BadStatus
74
+ raise # passthrough
75
+ rescue => e
76
+ set_debug_info(e.message, e.backtrace) if Gruf.backtrace_on_error
77
+ error_message = Gruf.use_exception_message ? e.message : Gruf.internal_error_message
78
+ fail!(:internal, :unknown, error_message)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,96 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ module Gruf
18
+ module Controllers
19
+ ##
20
+ # Encapsulates a request for a controller
21
+ #
22
+ class Request
23
+ # @var [Object] message
24
+ attr_reader :message
25
+ # @var [GRPC::ActiveCall] active_call
26
+ attr_reader :active_call
27
+ # @var [Symbol] method_key
28
+ attr_reader :method_key
29
+ # @var [Gruf::Controllers::Request::Type] type
30
+ attr_reader :type
31
+
32
+ delegate :metadata, to: :active_call
33
+ delegate :messages, :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :type
34
+
35
+ class Type
36
+ delegate :client_streamer?, :server_streamer?, :bidi_streamer?, :request_response?, to: :@rpc_desc
37
+
38
+ ##
39
+ # @param [GRPC::RpcDesc] rpc_desc
40
+ #
41
+ def initialize(rpc_desc)
42
+ @rpc_desc = rpc_desc
43
+ end
44
+ end
45
+
46
+ ##
47
+ # @param [Symbol] method_key The method symbol of the RPC method being executed
48
+ # @param [Class] service The class of the service being executed against
49
+ # @param [GRPC::RpcDesc] rpc_desc The RPC descriptor of the call
50
+ # @param [GRPC::ActiveCall] active_call The restricted view of the call
51
+ # @param [Object] message The protobuf message (or messages) of the request
52
+ #
53
+ def initialize(method_key:, service:, rpc_desc:, active_call:, message:)
54
+ @method_key = method_key
55
+ @service = service
56
+ @active_call = active_call
57
+ @message = message
58
+ @type = Type.new(rpc_desc)
59
+ end
60
+
61
+ ##
62
+ # Returns the service name as a translated name separated by periods. Strips
63
+ # the superfluous "Service" suffix from the name
64
+ #
65
+ # @return [String]
66
+ #
67
+ def service_key
68
+ @service.name.underscore.tr('/', '.').gsub('.service', '')
69
+ end
70
+
71
+ ##
72
+ # Parse the method signature into a service.method name format
73
+ #
74
+ # @return [String] The parsed service method name
75
+ #
76
+ def method_name
77
+ "#{service_key}.#{method_key}"
78
+ end
79
+
80
+ ##
81
+ # Return all messages for this request, properly handling different request types
82
+ #
83
+ # @return Enumerable<Object>
84
+ #
85
+ def messages
86
+ if client_streamer?
87
+ message.call { |msg| yield msg }
88
+ elsif bidi_streamer?
89
+ message
90
+ else
91
+ [message]
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,86 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ module Gruf
18
+ module Controllers
19
+ ##
20
+ # Binds gRPC services to a gruf controller
21
+ #
22
+ class ServiceBinder
23
+ ##
24
+ # Represents a bound RPC descriptor for future-proofing internal helpers
25
+ #
26
+ class BoundDesc < SimpleDelegator; end
27
+
28
+ ##
29
+ # @param [GRPC::GenericService] service
30
+ #
31
+ def initialize(service)
32
+ @service = service
33
+ end
34
+
35
+ ##
36
+ # @param [Gruf::Controllers::Base]
37
+ #
38
+ def bind!(controller)
39
+ rpc_methods.each { |name, desc| bind_method(controller, name, desc) }
40
+ end
41
+
42
+ private
43
+
44
+ ##
45
+ # @param [Gruf::Controllers::Base] controller
46
+ # @param [Symbol] method_name
47
+ # @param [BoundDesc] desc
48
+ #
49
+ def bind_method(controller, method_name, desc)
50
+ method_key = method_name.to_s.underscore.to_sym
51
+ service_ref = @service
52
+
53
+ @service.class_eval do
54
+ if desc.request_response?
55
+ define_method(method_key) do |message, active_call|
56
+ c = controller.new(method_key: method_key, service: service_ref, message: message, active_call: active_call, rpc_desc: desc)
57
+ c.call(method_key)
58
+ end
59
+ elsif desc.client_streamer?
60
+ define_method(method_key) do |active_call|
61
+ c = controller.new(method_key: method_key, service: service_ref, message: proc { |&block| active_call.each_remote_read(&block) }, active_call: active_call, rpc_desc: desc)
62
+ c.call(method_key)
63
+ end
64
+ elsif desc.server_streamer?
65
+ define_method(method_key) do |message, active_call, &block|
66
+ c = controller.new(method_key: method_key, service: service_ref, message: message, active_call: active_call, rpc_desc: desc)
67
+ c.call(method_key, &block)
68
+ end
69
+ else # bidi
70
+ define_method(method_key) do |messages, active_call, &block|
71
+ c = controller.new(method_key: method_key, service: service_ref, message: messages, active_call: active_call, rpc_desc: desc)
72
+ c.call(method_key, &block)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ ##
79
+ # @return Array<Gruf::Controllers::ServiceBinder::BoundDesc>
80
+ #
81
+ def rpc_methods
82
+ @service.rpc_descs.map { |rd| BoundDesc.new(rd) }
83
+ end
84
+ end
85
+ end
86
+ end
@@ -90,6 +90,15 @@ module Gruf
90
90
  @field_errors << Errors::Field.new(field_name, error_code, message)
91
91
  end
92
92
 
93
+ ##
94
+ # Return true if there are any present field errors
95
+ #
96
+ # @return [Boolean] True if the service has any field errors
97
+ #
98
+ def has_field_errors?
99
+ @field_errors.any?
100
+ end
101
+
93
102
  ##
94
103
  # Set the debugging information for the error message
95
104
  #
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ module Gruf
18
+ module Errors
19
+ module Helpers
20
+ delegate :add_field_error, :set_debug_info, :has_field_errors?, to: :error
21
+
22
+ ##
23
+ # Will issue a GRPC BadStatus exception, with a code based on the code passed.
24
+ #
25
+ # @param [Symbol] error_code The network error code that maps to gRPC status codes
26
+ # @param [Symbol] app_code The application-specific code for the error
27
+ # @param [String] message (Optional) A detail message about the error
28
+ # @param [Hash] metadata (Optional) Any metadata to inject into the trailing metadata for the response
29
+ #
30
+ def fail!(error_code, app_code = nil, message = '', metadata = {})
31
+ e = error
32
+ e.code = error_code.to_sym
33
+ e.app_code = app_code ? app_code.to_sym : e.code
34
+ e.message = message.to_s
35
+ e.metadata = metadata
36
+ e.fail!(request.active_call)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -15,25 +15,19 @@
15
15
  # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
16
  #
17
17
  module Gruf
18
- module Hooks
18
+ module Interceptors
19
19
  module ActiveRecord
20
20
  ##
21
21
  # Resets the ActiveRecord connection to maintain accurate connected state in the thread pool
22
22
  #
23
- class ConnectionReset < Gruf::Hooks::Base
23
+ class ConnectionReset < ::Gruf::Interceptors::ServerInterceptor
24
24
  ##
25
25
  # Reset any ActiveRecord connections after a gRPC service is called. Because of the way gRPC manages its
26
26
  # connection pool, we need to ensure that this is done to properly
27
27
  #
28
- # @param [Boolean] _success Whether or not the call was successful
29
- # @param [Object] _response The protobuf response object
30
- # @param [Symbol] _call_signature The gRPC method on the service that was called
31
- # @param [Object] _request The protobuf request object
32
- # @param [GRPC::ActiveCall] _call the gRPC core active call object, which represents marshalled data for
33
- # the call itself
34
- #
35
- def after(_success, _response, _call_signature, _request, _call)
28
+ def call
36
29
  ::ActiveRecord::Base.clear_active_connections! if enabled?
30
+ yield
37
31
  end
38
32
 
39
33
  private
@@ -0,0 +1,80 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ require 'base64'
18
+
19
+ module Gruf
20
+ module Interceptors
21
+ module Authentication
22
+ ##
23
+ # Handles basic authentication for gRPC requests
24
+ #
25
+ class Basic < Gruf::Interceptors::ServerInterceptor
26
+
27
+ def call
28
+ fail!(:unauthenticated, :unauthenticated) unless bypass? || valid?
29
+ yield
30
+ end
31
+
32
+ private
33
+
34
+ ##
35
+ # @return [Boolean] If this method is in the excluded list, bypass
36
+ #
37
+ def bypass?
38
+ options.fetch(:excluded_methods, []).include?(request.method_name)
39
+ end
40
+
41
+ ##
42
+ # @return [Boolean] True if the basic authentication was valid
43
+ #
44
+ def valid?
45
+ server_credentials.any? do |cred|
46
+ username = cred.fetch(:username, '').to_s
47
+ password = cred.fetch(:password, '').to_s
48
+ if username.empty?
49
+ request_password == password
50
+ else
51
+ request_credentials == "#{username}:#{password}"
52
+ end
53
+ end
54
+ end
55
+
56
+ ##
57
+ # @return [Array<Hash>] An array of valid server credentials for this service
58
+ #
59
+ def server_credentials
60
+ options.fetch(:credentials, [])
61
+ end
62
+
63
+ ##
64
+ # @return [String] The decoded request credentials
65
+ #
66
+ def request_credentials
67
+ credentials = request.active_call.respond_to?(:metadata) ? request.active_call.metadata['authorization'].to_s : ''
68
+ Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
69
+ end
70
+
71
+ ##
72
+ # @return [String] The decoded request password
73
+ #
74
+ def request_password
75
+ @request_password ||= request_credentials.split(':').last
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+ # Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
+ # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ #
9
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10
+ # Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13
+ # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15
+ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ #
17
+ module Gruf
18
+ module Interceptors
19
+ ##
20
+ # Base class for interception requests
21
+ #
22
+ class Base
23
+ # @var [Gruf::Controllers::Request] request
24
+ attr_reader :request
25
+ # @var [Gruf::Error] error
26
+ attr_reader :error
27
+ # @var [Hash] options
28
+ attr_reader :options
29
+
30
+ ##
31
+ # @param [Gruf::Controllers::Request] request
32
+ # @param [Gruf::Error] error
33
+ # @param [Hash] options
34
+ #
35
+ def initialize(request, error, options = {})
36
+ @request = request
37
+ @error = error
38
+ @options = options || {}
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ require_relative 'server_interceptor'
45
+ require_relative 'context'
46
+ require_relative 'timer'
47
+ require_relative 'active_record/connection_reset'
48
+ require_relative 'authentication/basic'
49
+ require_relative 'instrumentation/statsd'
50
+ require_relative 'instrumentation/output_metadata_timer'
51
+ require_relative 'instrumentation/request_logging/interceptor'