gruf 1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +81 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/README.md +312 -0
- data/bin/gruf +29 -0
- data/gruf.gemspec +41 -0
- data/lib/gruf.rb +38 -0
- data/lib/gruf/authentication.rb +57 -0
- data/lib/gruf/authentication/base.rb +57 -0
- data/lib/gruf/authentication/basic.rb +74 -0
- data/lib/gruf/authentication/none.rb +32 -0
- data/lib/gruf/authentication/strategies.rb +94 -0
- data/lib/gruf/client.rb +141 -0
- data/lib/gruf/configuration.rb +124 -0
- data/lib/gruf/error.rb +162 -0
- data/lib/gruf/errors/debug_info.rb +45 -0
- data/lib/gruf/errors/field.rb +48 -0
- data/lib/gruf/hooks/active_record/connection_reset.rb +36 -0
- data/lib/gruf/hooks/base.rb +44 -0
- data/lib/gruf/hooks/registry.rb +104 -0
- data/lib/gruf/instrumentation/base.rb +59 -0
- data/lib/gruf/instrumentation/output_metadata_timer.rb +40 -0
- data/lib/gruf/instrumentation/registry.rb +95 -0
- data/lib/gruf/instrumentation/statsd.rb +74 -0
- data/lib/gruf/loggable.rb +26 -0
- data/lib/gruf/logging.rb +33 -0
- data/lib/gruf/response.rb +55 -0
- data/lib/gruf/serializers/errors/base.rb +49 -0
- data/lib/gruf/serializers/errors/json.rb +39 -0
- data/lib/gruf/server.rb +92 -0
- data/lib/gruf/service.rb +271 -0
- data/lib/gruf/timer.rb +56 -0
- data/lib/gruf/version.rb +19 -0
- metadata +160 -0
data/bin/gruf
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
require 'rubygems'
|
19
|
+
require 'bundler/setup'
|
20
|
+
require 'gruf'
|
21
|
+
require 'rails' rescue nil
|
22
|
+
load 'config/environment.rb' if defined?(Rails)
|
23
|
+
|
24
|
+
begin
|
25
|
+
server = Gruf::Server.new
|
26
|
+
server.start!
|
27
|
+
rescue => e
|
28
|
+
Gruf.logger.fatal "FATAL ERROR: #{e.message} #{e.backtrace.join("\n")}"
|
29
|
+
end
|
data/gruf.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
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
|
+
$:.push File.expand_path("../lib", __FILE__)
|
18
|
+
require 'gruf/version'
|
19
|
+
|
20
|
+
Gem::Specification.new do |spec|
|
21
|
+
spec.name = 'gruf'
|
22
|
+
spec.version = Gruf::VERSION
|
23
|
+
spec.authors = ['Shaun McCormick']
|
24
|
+
spec.email = ['shaun.mccormick@bigcommerce.com']
|
25
|
+
|
26
|
+
spec.summary = %q{gRPC Ruby Framework}
|
27
|
+
spec.description = spec.summary
|
28
|
+
spec.homepage = 'https://github.com/bigcommerce/gruf'
|
29
|
+
|
30
|
+
spec.files = Dir['README.md', 'CHANGELOG.md', 'CODE_OF_CONDUCT.md', 'lib/**/*', 'gruf.gemspec']
|
31
|
+
spec.executables << 'gruf'
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
35
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
36
|
+
spec.add_development_dependency 'pry'
|
37
|
+
|
38
|
+
spec.add_runtime_dependency 'grpc', '~> 1.4'
|
39
|
+
spec.add_runtime_dependency 'grpc-tools', '~> 1.4'
|
40
|
+
spec.add_runtime_dependency 'activesupport'
|
41
|
+
end
|
data/lib/gruf.rb
ADDED
@@ -0,0 +1,38 @@
|
|
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 'grpc'
|
18
|
+
require 'active_support/core_ext/module/delegation'
|
19
|
+
require 'active_support/concern'
|
20
|
+
require 'active_support/inflector'
|
21
|
+
require 'base64'
|
22
|
+
require_relative 'gruf/version'
|
23
|
+
require_relative 'gruf/logging'
|
24
|
+
require_relative 'gruf/loggable'
|
25
|
+
require_relative 'gruf/configuration'
|
26
|
+
require_relative 'gruf/authentication'
|
27
|
+
require_relative 'gruf/hooks/registry'
|
28
|
+
require_relative 'gruf/instrumentation/registry'
|
29
|
+
require_relative 'gruf/service'
|
30
|
+
require_relative 'gruf/timer'
|
31
|
+
require_relative 'gruf/response'
|
32
|
+
require_relative 'gruf/error'
|
33
|
+
require_relative 'gruf/client'
|
34
|
+
require_relative 'gruf/server'
|
35
|
+
|
36
|
+
module Gruf
|
37
|
+
extend Configuration
|
38
|
+
end
|
@@ -0,0 +1,57 @@
|
|
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 'authentication/strategies'
|
18
|
+
require_relative 'authentication/base'
|
19
|
+
require_relative 'authentication/basic'
|
20
|
+
require_relative 'authentication/none'
|
21
|
+
|
22
|
+
module Gruf
|
23
|
+
module Authentication
|
24
|
+
class UnauthorizedError < StandardError; end
|
25
|
+
|
26
|
+
##
|
27
|
+
# @param [GRPC::ActiveCall] call
|
28
|
+
# @param [Symbol] strategy The authentication strategy to use
|
29
|
+
# @return [Boolean]
|
30
|
+
#
|
31
|
+
def self.verify(call)
|
32
|
+
credentials = call && call.respond_to?(:metadata) ? call.metadata[Gruf.authorization_metadata_key] : nil
|
33
|
+
|
34
|
+
if Gruf::Authentication::Strategies.any?
|
35
|
+
verified = false
|
36
|
+
Gruf::Authentication::Strategies.each do |_label, klass|
|
37
|
+
begin
|
38
|
+
# if a strategy passes, we've successfully authenticated and can proceed
|
39
|
+
if klass.verify(call, credentials)
|
40
|
+
verified = true
|
41
|
+
break
|
42
|
+
end
|
43
|
+
rescue => e
|
44
|
+
Gruf.logger.error "#{e.message} - #{e.backtrace[0..4].join("\n")}"
|
45
|
+
# NOOP, we don't want to fail other strategies because of a bad neighbor
|
46
|
+
# or if a strategy throws an exception because of a failed auth
|
47
|
+
# we should just proceed to the next strategy
|
48
|
+
end
|
49
|
+
end
|
50
|
+
verified
|
51
|
+
else
|
52
|
+
# we're not using any strategies, so no auth
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
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 Authentication
|
19
|
+
##
|
20
|
+
# Base interface for Authentication strategies
|
21
|
+
#
|
22
|
+
class Base
|
23
|
+
include Gruf::Loggable
|
24
|
+
|
25
|
+
attr_reader :credentials, :options
|
26
|
+
|
27
|
+
##
|
28
|
+
#
|
29
|
+
# @param [String] credentials
|
30
|
+
#
|
31
|
+
def initialize(credentials, options = {})
|
32
|
+
opts = Gruf.authentication_options || {}
|
33
|
+
@credentials = credentials
|
34
|
+
@options = opts.merge(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Verify the credentials. Helper class method.
|
39
|
+
#
|
40
|
+
# @param [GRPC::ActiveCall] call
|
41
|
+
# @param [String] credentials
|
42
|
+
# @param [Hash] options
|
43
|
+
#
|
44
|
+
def self.verify(call, credentials = '', options = {})
|
45
|
+
new(credentials, options).valid?(call)
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# @param [GRPC::ActiveCall] _call
|
50
|
+
# @return [Boolean]
|
51
|
+
#
|
52
|
+
def valid?(_call)
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,74 @@
|
|
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 Authentication
|
21
|
+
##
|
22
|
+
# Handles basic authentication for gRPC requests
|
23
|
+
#
|
24
|
+
class Basic < Base
|
25
|
+
##
|
26
|
+
# @param [GRPC::ActiveCall] _call
|
27
|
+
# @return [Boolean]
|
28
|
+
#
|
29
|
+
def valid?(_call)
|
30
|
+
server_credentials.any? do |cred|
|
31
|
+
username = cred.fetch(:username, '').to_s
|
32
|
+
password = cred.fetch(:password, '').to_s
|
33
|
+
if username.empty?
|
34
|
+
request_password == password
|
35
|
+
else
|
36
|
+
request_credentials == "#{username}:#{password}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return [Array<Hash>]
|
45
|
+
#
|
46
|
+
def server_credentials
|
47
|
+
options.fetch(:credentials, [])
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @return [String]
|
52
|
+
#
|
53
|
+
def request_credentials
|
54
|
+
Base64.decode64(credentials.to_s.gsub('Basic ', '').strip)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# @return [String]
|
59
|
+
# @deprecated
|
60
|
+
# :nocov:
|
61
|
+
def request_username
|
62
|
+
@request_username ||= request_credentials.split(':').first
|
63
|
+
end
|
64
|
+
# :nocov:
|
65
|
+
|
66
|
+
##
|
67
|
+
# @return [String]
|
68
|
+
#
|
69
|
+
def request_password
|
70
|
+
@request_password ||= request_credentials.split(':').last
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,32 @@
|
|
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 Authentication
|
19
|
+
##
|
20
|
+
# No authentication strategy
|
21
|
+
#
|
22
|
+
class None < Base
|
23
|
+
##
|
24
|
+
# @param [GRPC::ActiveCall] _call
|
25
|
+
# @return [TrueClass]
|
26
|
+
#
|
27
|
+
def valid?(_call)
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,94 @@
|
|
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 Authentication
|
19
|
+
##
|
20
|
+
# Provides a modifiable repository of strategies for authentication
|
21
|
+
#
|
22
|
+
class Strategies
|
23
|
+
class StrategyDescendantError < StandardError; end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
##
|
27
|
+
# Add an authentication strategy, either through a class or a block
|
28
|
+
#
|
29
|
+
# @param [String] name
|
30
|
+
# @param [Class|NilClass] strategy
|
31
|
+
# @return [Class]
|
32
|
+
#
|
33
|
+
def add(name, strategy = nil, &block)
|
34
|
+
base = Gruf::Authentication::Base
|
35
|
+
strategy ||= Class.new(base)
|
36
|
+
strategy.class_eval(&block) if block_given?
|
37
|
+
|
38
|
+
# all strategies require the valid? method
|
39
|
+
raise NoMethodError unless strategy.method_defined?(:valid?)
|
40
|
+
|
41
|
+
raise StrategyDescendantError, "Strategies must descend from #{base}" unless strategy.ancestors.include?(base)
|
42
|
+
|
43
|
+
_strategies[name.to_sym] = strategy
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Return a strategy via a hash accessor syntax
|
48
|
+
#
|
49
|
+
def [](label)
|
50
|
+
_strategies[label.to_sym]
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Iterate over each
|
55
|
+
#
|
56
|
+
def each
|
57
|
+
_strategies.each do |s|
|
58
|
+
yield s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# @return [Hash<Class>]
|
64
|
+
#
|
65
|
+
def to_h
|
66
|
+
_strategies
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# @return [Boolean]
|
71
|
+
#
|
72
|
+
def any?
|
73
|
+
to_h.keys.count > 0
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# @return [Hash]
|
78
|
+
#
|
79
|
+
def clear
|
80
|
+
@strategies = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
##
|
86
|
+
# @return [Hash<Class>]
|
87
|
+
#
|
88
|
+
def _strategies
|
89
|
+
@strategies ||= {}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/gruf/client.rb
ADDED
@@ -0,0 +1,141 @@
|
|
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
|
+
class Client < SimpleDelegator
|
19
|
+
include Gruf::Loggable
|
20
|
+
|
21
|
+
class Error < StandardError
|
22
|
+
attr_reader :error
|
23
|
+
|
24
|
+
def initialize(error)
|
25
|
+
@error = error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :base_klass, :service_klass, :opts
|
30
|
+
|
31
|
+
##
|
32
|
+
# @param [Module] service The namespace of the client Stub that is desired to load
|
33
|
+
# @param [Hash] options
|
34
|
+
# @option options [String] :password The password for basic authentication for the service.
|
35
|
+
# @option options [String] :hostname The hostname of the service. Defaults to linkerd.
|
36
|
+
#
|
37
|
+
def initialize(service:, options: {})
|
38
|
+
@base_klass = service
|
39
|
+
@service_klass = "#{base_klass}::Service".constantize
|
40
|
+
@opts = options || {}
|
41
|
+
@opts[:password] = options.fetch(:password, '').to_s
|
42
|
+
@opts[:hostname] = options.fetch(:hostname, Gruf.default_client_host)
|
43
|
+
client = "#{service}::Stub".constantize.new(@opts[:hostname], build_ssl_credentials)
|
44
|
+
super(client)
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Call the client's method with given params
|
49
|
+
#
|
50
|
+
def call(request_method, params = {}, metadata = {})
|
51
|
+
req = request_object(request_method, params)
|
52
|
+
md = build_metadata(metadata)
|
53
|
+
call_sig = call_signature(request_method)
|
54
|
+
|
55
|
+
raise NotImplementedError, "The method #{request_method} has not been implemented in this service." unless call_sig
|
56
|
+
|
57
|
+
execute(call_sig, req, md)
|
58
|
+
rescue GRPC::BadStatus => e
|
59
|
+
emk = Gruf.error_metadata_key.to_s
|
60
|
+
raise Gruf::Client::Error, error_deserializer_class.new(e.metadata[emk]).deserialize if e.respond_to?(:metadata) && e.metadata.key?(emk)
|
61
|
+
raise # passthrough
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
##
|
67
|
+
# @param [Symbol] call_sig The call signature being executed
|
68
|
+
# @param [Object] req
|
69
|
+
# @param [Hash] md
|
70
|
+
#
|
71
|
+
def execute(call_sig, req, md)
|
72
|
+
timed = Timer.time do
|
73
|
+
send(call_sig, req, return_op: true, metadata: md)
|
74
|
+
end
|
75
|
+
Gruf::Response.new(timed.result, timed.time)
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# @return [Struct<GRPC::RpcDesc>]
|
80
|
+
#
|
81
|
+
def rpc_desc(request_method)
|
82
|
+
service_klass.rpc_descs[request_method.to_sym]
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# @return [Class] The request object
|
87
|
+
#
|
88
|
+
def request_object(request_method, params = {})
|
89
|
+
desc = rpc_desc(request_method)
|
90
|
+
desc && desc.input ? desc.input.new(params) : nil
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# @return [Symbol]
|
95
|
+
#
|
96
|
+
def call_signature(request_method)
|
97
|
+
desc = rpc_desc(request_method)
|
98
|
+
desc && desc.name ? desc.name.to_s.underscore.to_sym : nil
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# @return [Hash]
|
103
|
+
#
|
104
|
+
def build_metadata(metadata = {})
|
105
|
+
unless opts[:password].empty?
|
106
|
+
username = opts.fetch(:username, 'grpc').to_s
|
107
|
+
username = username.empty? ? '' : "#{username}:"
|
108
|
+
auth_string = Base64.encode64("#{username}#{opts[:password]}")
|
109
|
+
metadata[:authorization] = "Basic #{auth_string}".tr("\n", '')
|
110
|
+
end
|
111
|
+
metadata
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# @return [Symbol|GRPC::Core::ChannelCredentials]
|
116
|
+
#
|
117
|
+
# :nocov:
|
118
|
+
def build_ssl_credentials
|
119
|
+
cert = nil
|
120
|
+
if opts[:ssl_certificate_file]
|
121
|
+
cert = File.read(opts[:ssl_certificate_file]).to_s.strip
|
122
|
+
elsif opts[:ssl_certificate]
|
123
|
+
cert = opts[:ssl_certificate].to_s.strip
|
124
|
+
end
|
125
|
+
|
126
|
+
cert ? GRPC::Core::ChannelCredentials.new(cert) : :this_channel_is_insecure
|
127
|
+
end
|
128
|
+
# :nocov:
|
129
|
+
|
130
|
+
##
|
131
|
+
# @return [Class]
|
132
|
+
#
|
133
|
+
def error_deserializer_class
|
134
|
+
if Gruf.error_serializer
|
135
|
+
Gruf.error_serializer.is_a?(Class) ? Gruf.error_serializer : Gruf.error_serializer.to_s.constantize
|
136
|
+
else
|
137
|
+
Gruf::Serializers::Errors::Json
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|