protobuf-cucumber 3.10.4
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/.gitignore +21 -0
- data/.rubocop.yml +70 -0
- data/.rubocop_todo.yml +145 -0
- data/.travis.yml +40 -0
- data/.yardopts +5 -0
- data/CHANGES.md +344 -0
- data/CONTRIBUTING.md +16 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +64 -0
- data/bin/protoc-gen-ruby +22 -0
- data/bin/rpc_server +5 -0
- data/install-protobuf.sh +28 -0
- data/lib/protobuf.rb +129 -0
- data/lib/protobuf/cli.rb +257 -0
- data/lib/protobuf/code_generator.rb +120 -0
- data/lib/protobuf/decoder.rb +28 -0
- data/lib/protobuf/deprecation.rb +117 -0
- data/lib/protobuf/descriptors.rb +3 -0
- data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
- data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
- data/lib/protobuf/encoder.rb +11 -0
- data/lib/protobuf/enum.rb +365 -0
- data/lib/protobuf/exceptions.rb +9 -0
- data/lib/protobuf/field.rb +74 -0
- data/lib/protobuf/field/base_field.rb +380 -0
- data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
- data/lib/protobuf/field/bool_field.rb +64 -0
- data/lib/protobuf/field/bytes_field.rb +78 -0
- data/lib/protobuf/field/double_field.rb +25 -0
- data/lib/protobuf/field/enum_field.rb +61 -0
- data/lib/protobuf/field/field_array.rb +104 -0
- data/lib/protobuf/field/field_hash.rb +122 -0
- data/lib/protobuf/field/fixed32_field.rb +25 -0
- data/lib/protobuf/field/fixed64_field.rb +28 -0
- data/lib/protobuf/field/float_field.rb +43 -0
- data/lib/protobuf/field/int32_field.rb +21 -0
- data/lib/protobuf/field/int64_field.rb +34 -0
- data/lib/protobuf/field/integer_field.rb +23 -0
- data/lib/protobuf/field/message_field.rb +51 -0
- data/lib/protobuf/field/sfixed32_field.rb +27 -0
- data/lib/protobuf/field/sfixed64_field.rb +28 -0
- data/lib/protobuf/field/signed_integer_field.rb +29 -0
- data/lib/protobuf/field/sint32_field.rb +21 -0
- data/lib/protobuf/field/sint64_field.rb +21 -0
- data/lib/protobuf/field/string_field.rb +51 -0
- data/lib/protobuf/field/uint32_field.rb +21 -0
- data/lib/protobuf/field/uint64_field.rb +21 -0
- data/lib/protobuf/field/varint_field.rb +77 -0
- data/lib/protobuf/generators/base.rb +85 -0
- data/lib/protobuf/generators/enum_generator.rb +39 -0
- data/lib/protobuf/generators/extension_generator.rb +27 -0
- data/lib/protobuf/generators/field_generator.rb +193 -0
- data/lib/protobuf/generators/file_generator.rb +262 -0
- data/lib/protobuf/generators/group_generator.rb +122 -0
- data/lib/protobuf/generators/message_generator.rb +104 -0
- data/lib/protobuf/generators/option_generator.rb +17 -0
- data/lib/protobuf/generators/printable.rb +160 -0
- data/lib/protobuf/generators/service_generator.rb +50 -0
- data/lib/protobuf/lifecycle.rb +33 -0
- data/lib/protobuf/logging.rb +39 -0
- data/lib/protobuf/message.rb +260 -0
- data/lib/protobuf/message/fields.rb +233 -0
- data/lib/protobuf/message/serialization.rb +85 -0
- data/lib/protobuf/optionable.rb +70 -0
- data/lib/protobuf/rpc/buffer.rb +78 -0
- data/lib/protobuf/rpc/client.rb +140 -0
- data/lib/protobuf/rpc/connectors/base.rb +221 -0
- data/lib/protobuf/rpc/connectors/ping.rb +89 -0
- data/lib/protobuf/rpc/connectors/socket.rb +78 -0
- data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
- data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
- data/lib/protobuf/rpc/env.rb +60 -0
- data/lib/protobuf/rpc/error.rb +28 -0
- data/lib/protobuf/rpc/error/client_error.rb +31 -0
- data/lib/protobuf/rpc/error/server_error.rb +43 -0
- data/lib/protobuf/rpc/middleware.rb +25 -0
- data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
- data/lib/protobuf/rpc/middleware/logger.rb +95 -0
- data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
- data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
- data/lib/protobuf/rpc/middleware/runner.rb +18 -0
- data/lib/protobuf/rpc/rpc.pb.rb +64 -0
- data/lib/protobuf/rpc/rpc_method.rb +16 -0
- data/lib/protobuf/rpc/server.rb +39 -0
- data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
- data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
- data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
- data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
- data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
- data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
- data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
- data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
- data/lib/protobuf/rpc/service.rb +172 -0
- data/lib/protobuf/rpc/service_directory.rb +261 -0
- data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
- data/lib/protobuf/rpc/service_filters.rb +250 -0
- data/lib/protobuf/rpc/stat.rb +119 -0
- data/lib/protobuf/socket.rb +21 -0
- data/lib/protobuf/tasks.rb +1 -0
- data/lib/protobuf/tasks/compile.rake +80 -0
- data/lib/protobuf/varint.rb +20 -0
- data/lib/protobuf/varint_pure.rb +31 -0
- data/lib/protobuf/version.rb +3 -0
- data/lib/protobuf/wire_type.rb +10 -0
- data/lib/protobuf/zmq.rb +21 -0
- data/profile.html +5103 -0
- data/proto/dynamic_discovery.proto +44 -0
- data/proto/google/protobuf/compiler/plugin.proto +147 -0
- data/proto/google/protobuf/descriptor.proto +779 -0
- data/proto/rpc.proto +69 -0
- data/protobuf-cucumber.gemspec +57 -0
- data/spec/benchmark/tasks.rb +143 -0
- data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
- data/spec/encoding/all_types_spec.rb +103 -0
- data/spec/encoding/extreme_values_spec.rb +0 -0
- data/spec/functional/class_inheritance_spec.rb +52 -0
- data/spec/functional/code_generator_spec.rb +58 -0
- data/spec/functional/socket_server_spec.rb +59 -0
- data/spec/functional/zmq_server_spec.rb +105 -0
- data/spec/lib/protobuf/cli_spec.rb +317 -0
- data/spec/lib/protobuf/code_generator_spec.rb +87 -0
- data/spec/lib/protobuf/enum_spec.rb +307 -0
- data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
- data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
- data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
- data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
- data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
- data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
- data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
- data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
- data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
- data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
- data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
- data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
- data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
- data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
- data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
- data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
- data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
- data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
- data/spec/lib/protobuf/field_spec.rb +192 -0
- data/spec/lib/protobuf/generators/base_spec.rb +154 -0
- data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
- data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
- data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
- data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
- data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
- data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
- data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
- data/spec/lib/protobuf/message_spec.rb +944 -0
- data/spec/lib/protobuf/optionable_spec.rb +265 -0
- data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
- data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
- data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
- data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
- data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
- data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
- data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
- data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
- data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
- data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
- data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
- data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
- data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
- data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
- data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
- data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
- data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
- data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
- data/spec/lib/protobuf/varint_spec.rb +29 -0
- data/spec/lib/protobuf_spec.rb +105 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/all.rb +6 -0
- data/spec/support/packed_field.rb +23 -0
- data/spec/support/protos/all_types.data.bin +0 -0
- data/spec/support/protos/all_types.data.txt +119 -0
- data/spec/support/protos/enum.pb.rb +63 -0
- data/spec/support/protos/enum.proto +37 -0
- data/spec/support/protos/extreme_values.data.bin +0 -0
- data/spec/support/protos/google_unittest.bin +0 -0
- data/spec/support/protos/google_unittest.pb.rb +798 -0
- data/spec/support/protos/google_unittest.proto +884 -0
- data/spec/support/protos/google_unittest_custom_options.bin +0 -0
- data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
- data/spec/support/protos/google_unittest_custom_options.proto +424 -0
- data/spec/support/protos/google_unittest_import.pb.rb +55 -0
- data/spec/support/protos/google_unittest_import.proto +73 -0
- data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
- data/spec/support/protos/google_unittest_import_public.proto +41 -0
- data/spec/support/protos/map-test.bin +157 -0
- data/spec/support/protos/map-test.pb.rb +85 -0
- data/spec/support/protos/map-test.proto +68 -0
- data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
- data/spec/support/protos/multi_field_extensions.proto +35 -0
- data/spec/support/protos/resource.pb.rb +172 -0
- data/spec/support/protos/resource.proto +137 -0
- data/spec/support/resource_service.rb +23 -0
- data/spec/support/server.rb +65 -0
- data/spec/support/test_app_file.rb +2 -0
- data/varint_prof.rb +82 -0
- metadata +579 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'protobuf/decoder'
|
3
|
+
require 'protobuf/encoder'
|
4
|
+
|
5
|
+
module Protobuf
|
6
|
+
class Message
|
7
|
+
module Serialization
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def decode(bytes)
|
11
|
+
new.decode(bytes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def decode_from(stream)
|
15
|
+
new.decode_from(stream)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create a new object with the given values and return the encoded bytes.
|
19
|
+
def encode(fields = {})
|
20
|
+
new(fields).encode
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.included(other)
|
25
|
+
other.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Instance Methods
|
30
|
+
#
|
31
|
+
|
32
|
+
# Decode the given non-stream bytes into this message.
|
33
|
+
#
|
34
|
+
def decode(bytes)
|
35
|
+
decode_from(::StringIO.new(bytes))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Decode the given stream into this message.
|
39
|
+
#
|
40
|
+
def decode_from(stream)
|
41
|
+
::Protobuf::Decoder.decode_each_field(stream) do |tag, bytes|
|
42
|
+
set_field_bytes(tag, bytes)
|
43
|
+
end
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Encode this message
|
49
|
+
#
|
50
|
+
def encode
|
51
|
+
stream = ::StringIO.new
|
52
|
+
stream.set_encoding(::Protobuf::Field::BytesField::BYTES_ENCODING)
|
53
|
+
encode_to(stream)
|
54
|
+
stream.string
|
55
|
+
end
|
56
|
+
|
57
|
+
# Encode this message to the given stream.
|
58
|
+
#
|
59
|
+
def encode_to(stream)
|
60
|
+
::Protobuf::Encoder.encode(self, stream)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Instance Aliases
|
65
|
+
#
|
66
|
+
alias :parse_from_string decode
|
67
|
+
alias :deserialize decode
|
68
|
+
alias :parse_from decode_from
|
69
|
+
alias :deserialize_from decode_from
|
70
|
+
alias :to_s encode
|
71
|
+
alias :bytes encode
|
72
|
+
alias :serialize encode
|
73
|
+
alias :serialize_to_string encode
|
74
|
+
alias :serialize_to encode_to
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def set_field_bytes(tag, bytes)
|
79
|
+
field = _protobuf_message_field[tag]
|
80
|
+
field.set(self, bytes) if field
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Protobuf
|
2
|
+
module Optionable
|
3
|
+
module ClassMethods
|
4
|
+
def get_option(name)
|
5
|
+
name = name.to_s
|
6
|
+
option = optionable_descriptor_class.get_field(name, true)
|
7
|
+
fail ArgumentError, "invalid option=#{name}" unless option
|
8
|
+
unless option.fully_qualified_name.to_s == name
|
9
|
+
# Eventually we'll deprecate the use of simple names of fields completely, but for now make sure people
|
10
|
+
# are accessing options correctly. We allow simple names in other places for backwards compatibility.
|
11
|
+
fail ArgumentError, "must access option using its fully qualified name: #{option.fully_qualified_name.inspect}"
|
12
|
+
end
|
13
|
+
value =
|
14
|
+
if @_optionable_options.try(:key?, name)
|
15
|
+
@_optionable_options[name]
|
16
|
+
else
|
17
|
+
option.default_value
|
18
|
+
end
|
19
|
+
if option.type_class < ::Protobuf::Message
|
20
|
+
option.type_class.new(value)
|
21
|
+
else
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_option!(name)
|
27
|
+
get_option(name) if @_optionable_options.try(:key?, name.to_s)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def set_option(name, value = true)
|
33
|
+
@_optionable_options ||= {}
|
34
|
+
@_optionable_options[name.to_s] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_option(name)
|
39
|
+
self.class.get_option(name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_option!(name)
|
43
|
+
self.class.get_option!(name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.inject(base_class, extend_class = true, &block)
|
47
|
+
unless block_given?
|
48
|
+
fail ArgumentError, 'missing option class block (e.g: ::Google::Protobuf::MessageOptions)'
|
49
|
+
end
|
50
|
+
if extend_class
|
51
|
+
# Check if optionable_descriptor_class is already defined and short circuit if so.
|
52
|
+
# File options are injected per module, and since a module can be defined more than once,
|
53
|
+
# we will get a warning if we try to define optionable_descriptor_class twice.
|
54
|
+
if base_class.respond_to?(:optionable_descriptor_class)
|
55
|
+
# Don't define optionable_descriptor_class twice
|
56
|
+
return if base_class.optionable_descriptor_class == block.call
|
57
|
+
|
58
|
+
fail 'A class is being defined with two different descriptor classes, something is very wrong'
|
59
|
+
end
|
60
|
+
|
61
|
+
base_class.extend(ClassMethods)
|
62
|
+
base_class.__send__(:include, self)
|
63
|
+
base_class.define_singleton_method(:optionable_descriptor_class, block)
|
64
|
+
else
|
65
|
+
base_class.__send__(:include, ClassMethods)
|
66
|
+
base_class.module_eval { define_method(:optionable_descriptor_class, block) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Protobuf
|
2
|
+
module Rpc
|
3
|
+
class Buffer
|
4
|
+
|
5
|
+
attr_accessor :mode, :data, :size
|
6
|
+
|
7
|
+
MODES = [:read, :write].freeze
|
8
|
+
|
9
|
+
# constantize this so we don't re-initialize the regex every time we need it
|
10
|
+
SIZE_REGEX = /^\d+-/
|
11
|
+
|
12
|
+
def initialize(mode = :read)
|
13
|
+
@flush = false
|
14
|
+
@data = ""
|
15
|
+
@size = 0
|
16
|
+
self.mode = mode
|
17
|
+
end
|
18
|
+
|
19
|
+
def mode=(mode)
|
20
|
+
@mode =
|
21
|
+
if MODES.include?(mode)
|
22
|
+
mode
|
23
|
+
else
|
24
|
+
:read
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def write(force_mode = true)
|
29
|
+
if force_mode && reading?
|
30
|
+
self.mode = :write
|
31
|
+
elsif !force_mode && reading?
|
32
|
+
fail 'You chose to write the buffer when in read mode'
|
33
|
+
end
|
34
|
+
|
35
|
+
@size = @data.length
|
36
|
+
"#{@size}-#{@data}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def <<(data)
|
40
|
+
@data << data
|
41
|
+
if reading?
|
42
|
+
get_data_size
|
43
|
+
check_for_flush
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_data(data) # rubocop:disable Style/AccessorMethodName
|
48
|
+
@data = data.to_s
|
49
|
+
@size = @data.size
|
50
|
+
end
|
51
|
+
|
52
|
+
def reading?
|
53
|
+
mode == :read
|
54
|
+
end
|
55
|
+
|
56
|
+
def writing?
|
57
|
+
mode == :write
|
58
|
+
end
|
59
|
+
|
60
|
+
def flushed?
|
61
|
+
@flush
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_data_size # rubocop:disable Style/AccessorMethodName
|
65
|
+
if @size == 0 || @data.match(SIZE_REGEX)
|
66
|
+
sliced_size = @data.slice!(SIZE_REGEX)
|
67
|
+
@size = sliced_size.delete('-').to_i unless sliced_size.nil?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def check_for_flush
|
74
|
+
@flush = true if !@size.nil? && @data.length == @size
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'protobuf'
|
3
|
+
require 'protobuf/logging'
|
4
|
+
require 'protobuf/rpc/error'
|
5
|
+
|
6
|
+
module Protobuf
|
7
|
+
module Rpc
|
8
|
+
class Client
|
9
|
+
extend Forwardable
|
10
|
+
include Protobuf::Logging
|
11
|
+
|
12
|
+
def_delegators :@connector, :options, :complete_cb, :success_cb, :failure_cb, :send_request
|
13
|
+
attr_reader :connector
|
14
|
+
|
15
|
+
# Create a new client with default options (defined in ClientConnection)
|
16
|
+
# See Service#client for a more convenient way to create a client, as well
|
17
|
+
# as Client#method_missing defined below.
|
18
|
+
#
|
19
|
+
# request = WidgetFindRequest.new
|
20
|
+
# client = Client.new({
|
21
|
+
# :service => WidgetService,
|
22
|
+
# :method => "find",
|
23
|
+
# :request_type => "WidgetFindRequest",
|
24
|
+
# :response_type => "WidgetList",
|
25
|
+
# :request => request
|
26
|
+
# })
|
27
|
+
#
|
28
|
+
def initialize(options = {})
|
29
|
+
fail "Invalid client configuration. Service must be defined." if options[:service].nil?
|
30
|
+
@connector = ::Protobuf.connector_type_class.new(options)
|
31
|
+
logger.debug { sign_message("Initialized with options: #{options.inspect}") }
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_signature
|
35
|
+
@_log_signature ||= "[client-#{self.class}]"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set a complete callback on the client to return the object (self).
|
39
|
+
#
|
40
|
+
# client = Client.new(:service => WidgetService)
|
41
|
+
# client.on_complete {|obj| ... }
|
42
|
+
#
|
43
|
+
def on_complete(&complete_cb)
|
44
|
+
@connector.complete_cb = complete_cb
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_complete=(callable)
|
48
|
+
if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
|
49
|
+
fail "callable must take a single argument and respond to :call"
|
50
|
+
end
|
51
|
+
|
52
|
+
@connector.complete_cb = callable
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set a failure callback on the client to return the
|
56
|
+
# error returned by the service, if any. If this callback
|
57
|
+
# is called, success_cb will NOT be called.
|
58
|
+
#
|
59
|
+
# client = Client.new(:service => WidgetService)
|
60
|
+
# client.on_failure {|err| ... }
|
61
|
+
#
|
62
|
+
def on_failure(&failure_cb)
|
63
|
+
@connector.failure_cb = failure_cb
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_failure=(callable)
|
67
|
+
if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
|
68
|
+
fail "Callable must take a single argument and respond to :call"
|
69
|
+
end
|
70
|
+
|
71
|
+
@connector.failure_cb = callable
|
72
|
+
end
|
73
|
+
|
74
|
+
# Set a success callback on the client to return the
|
75
|
+
# successful response from the service when it is returned.
|
76
|
+
# If this callback is called, failure_cb will NOT be called.
|
77
|
+
#
|
78
|
+
# client = Client.new(:service => WidgetService)
|
79
|
+
# client.on_success {|res| ... }
|
80
|
+
#
|
81
|
+
def on_success(&success_cb)
|
82
|
+
@connector.success_cb = success_cb
|
83
|
+
end
|
84
|
+
|
85
|
+
def on_success=(callable)
|
86
|
+
if !callable.nil? && !callable.respond_to?(:call) && callable.arity != 1
|
87
|
+
fail "Callable must take a single argument and respond to :call"
|
88
|
+
end
|
89
|
+
|
90
|
+
@connector.success_cb = callable
|
91
|
+
end
|
92
|
+
|
93
|
+
# Provides a mechanism to call the service method against the client
|
94
|
+
# which will automatically setup the service_class and method_name
|
95
|
+
# in the wrapper protobuf request.
|
96
|
+
#
|
97
|
+
# # The :find method is not defined by Client which will trigger method_missing
|
98
|
+
# Client.new(:service => WidgetService).find do |c|
|
99
|
+
# # This block will be invoked before the request is made
|
100
|
+
# # `c` in this case is the client object you created above
|
101
|
+
# c.on_success {|res| ... }
|
102
|
+
# c.on_failure {|err| ... }
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
def method_missing(method_name, *params)
|
106
|
+
service = options[:service]
|
107
|
+
if service.rpc_method?(method_name)
|
108
|
+
logger.debug { sign_message("#{service.name}##{method_name}") }
|
109
|
+
rpc = service.rpcs[method_name.to_sym]
|
110
|
+
|
111
|
+
options[:request_type] = rpc.request_type
|
112
|
+
logger.debug { sign_message("Request Type: #{options[:request_type].name}") }
|
113
|
+
|
114
|
+
options[:response_type] = rpc.response_type
|
115
|
+
logger.debug { sign_message("Response Type: #{options[:response_type].name}") }
|
116
|
+
|
117
|
+
options[:method] = method_name.to_s
|
118
|
+
options[:request] = params[0].is_a?(Hash) ? options[:request_type].new(params[0]) : params[0]
|
119
|
+
logger.debug { sign_message("Request Data: #{options[:request].inspect}") }
|
120
|
+
|
121
|
+
# Call client to setup on_success and on_failure event callbacks
|
122
|
+
if block_given?
|
123
|
+
logger.debug { sign_message("client setup callback given, invoking") }
|
124
|
+
yield(self)
|
125
|
+
else
|
126
|
+
logger.debug { sign_message("no block given for callbacks") }
|
127
|
+
end
|
128
|
+
|
129
|
+
send_request
|
130
|
+
else
|
131
|
+
logger.error { sign_message("#{service.name}##{method_name} not rpc method, passing to super") }
|
132
|
+
super(method_name, *params)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
ActiveSupport.run_load_hooks(:protobuf_rpc_client, Client)
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'protobuf/logging'
|
3
|
+
require 'protobuf/rpc/rpc.pb'
|
4
|
+
require 'protobuf/rpc/buffer'
|
5
|
+
require 'protobuf/rpc/error'
|
6
|
+
require 'protobuf/rpc/stat'
|
7
|
+
|
8
|
+
module Protobuf
|
9
|
+
module Rpc
|
10
|
+
module Connectors
|
11
|
+
DEFAULT_OPTIONS = {
|
12
|
+
:service => nil, # Fully-qualified Service class
|
13
|
+
:method => nil, # Service method to invoke
|
14
|
+
:host => '127.0.0.1', # The hostname or address of the service (usually overridden)
|
15
|
+
:port => '9399', # The port of the service (usually overridden or pre-configured)
|
16
|
+
:request => nil, # The request object sent by the client
|
17
|
+
:request_type => nil, # The request type expected by the client
|
18
|
+
:response_type => nil, # The response type expected by the client
|
19
|
+
:timeout => nil, # The timeout for the request, also handled by client.rb
|
20
|
+
:client_host => nil, # The hostname or address of this client
|
21
|
+
:first_alive_load_balance => false, # Do we want to use check_avail frames before request
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
class Base
|
25
|
+
include Protobuf::Logging
|
26
|
+
|
27
|
+
attr_reader :options, :error
|
28
|
+
attr_accessor :success_cb, :failure_cb, :complete_cb, :stats
|
29
|
+
|
30
|
+
def initialize(options)
|
31
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
32
|
+
@stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
|
33
|
+
end
|
34
|
+
|
35
|
+
def any_callbacks?
|
36
|
+
[@complete_cb, @failure_cb, @success_cb].any?
|
37
|
+
end
|
38
|
+
|
39
|
+
def close_connection
|
40
|
+
fail 'If you inherit a Connector from Base you must implement close_connection'
|
41
|
+
end
|
42
|
+
|
43
|
+
def complete
|
44
|
+
@stats.stop
|
45
|
+
logger.info { @stats.to_s }
|
46
|
+
logger.debug { sign_message('Response proceessing complete') }
|
47
|
+
@complete_cb.call(self) unless @complete_cb.nil?
|
48
|
+
rescue => e
|
49
|
+
logger.error { sign_message('Complete callback error encountered') }
|
50
|
+
log_exception(e)
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
def data_callback(data)
|
55
|
+
logger.debug { sign_message('Using data_callback') }
|
56
|
+
@used_data_callback = true
|
57
|
+
@data = data
|
58
|
+
end
|
59
|
+
|
60
|
+
# All failures should be routed through this method.
|
61
|
+
#
|
62
|
+
# @param [Symbol] code The code we're using (see ::Protobuf::Socketrpc::ErrorReason)
|
63
|
+
# @param [String] message The error message
|
64
|
+
def failure(code, message)
|
65
|
+
@error = ClientError.new
|
66
|
+
@stats.status = @error.code = ::Protobuf::Socketrpc::ErrorReason.fetch(code)
|
67
|
+
@error.message = message
|
68
|
+
|
69
|
+
logger.debug { sign_message("Server failed request (invoking on_failure): #{@error.inspect}") }
|
70
|
+
|
71
|
+
@failure_cb.call(@error) unless @failure_cb.nil?
|
72
|
+
rescue => e
|
73
|
+
logger.error { sign_message("Failure callback error encountered") }
|
74
|
+
log_exception(e)
|
75
|
+
raise
|
76
|
+
ensure
|
77
|
+
complete
|
78
|
+
end
|
79
|
+
|
80
|
+
def first_alive_load_balance?
|
81
|
+
ENV.key?("PB_FIRST_ALIVE_LOAD_BALANCE") ||
|
82
|
+
options[:first_alive_load_balance]
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize_stats
|
86
|
+
@stats = ::Protobuf::Rpc::Stat.new(:CLIENT)
|
87
|
+
@stats.server = [@options[:port], @options[:host]]
|
88
|
+
@stats.service = @options[:service].name
|
89
|
+
@stats.method_name = @options[:method].to_s
|
90
|
+
rescue => ex
|
91
|
+
log_exception(ex)
|
92
|
+
failure(:RPC_ERROR, "Invalid stats configuration. #{ex.message}")
|
93
|
+
end
|
94
|
+
|
95
|
+
def log_signature
|
96
|
+
@_log_signature ||= "[client-#{self.class}]"
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_response
|
100
|
+
# Close up the connection as we no longer need it
|
101
|
+
close_connection
|
102
|
+
|
103
|
+
logger.debug { sign_message("Parsing response from server (connection closed)") }
|
104
|
+
|
105
|
+
# Parse out the raw response
|
106
|
+
@stats.response_size = @response_data.size unless @response_data.nil?
|
107
|
+
response_wrapper = ::Protobuf::Socketrpc::Response.decode(@response_data)
|
108
|
+
@stats.server = response_wrapper.server if response_wrapper.field?(:server)
|
109
|
+
|
110
|
+
# Determine success or failure based on parsed data
|
111
|
+
if response_wrapper.field?(:error_reason)
|
112
|
+
logger.debug { sign_message("Error response parsed") }
|
113
|
+
|
114
|
+
# fail the call if we already know the client is failed
|
115
|
+
# (don't try to parse out the response payload)
|
116
|
+
failure(response_wrapper.error_reason, response_wrapper.error)
|
117
|
+
else
|
118
|
+
logger.debug { sign_message("Successful response parsed") }
|
119
|
+
|
120
|
+
# Ensure client_response is an instance
|
121
|
+
parsed = @options[:response_type].decode(response_wrapper.response_proto.to_s)
|
122
|
+
|
123
|
+
if parsed.nil? && !response_wrapper.field?(:error_reason)
|
124
|
+
failure(:BAD_RESPONSE_PROTO, 'Unable to parse response from server')
|
125
|
+
else
|
126
|
+
verify_callbacks
|
127
|
+
succeed(parsed)
|
128
|
+
return @data if @used_data_callback
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def ping_port
|
134
|
+
@ping_port ||= ENV["PB_RPC_PING_PORT"]
|
135
|
+
end
|
136
|
+
|
137
|
+
def ping_port_enabled?
|
138
|
+
ENV.key?("PB_RPC_PING_PORT")
|
139
|
+
end
|
140
|
+
|
141
|
+
def request_bytes
|
142
|
+
validate_request_type!
|
143
|
+
return ::Protobuf::Socketrpc::Request.encode(request_fields)
|
144
|
+
rescue => e
|
145
|
+
failure(:INVALID_REQUEST_PROTO, "Could not set request proto: #{e.message}")
|
146
|
+
end
|
147
|
+
|
148
|
+
def request_caller
|
149
|
+
@options[:client_host] || ::Protobuf.client_host
|
150
|
+
end
|
151
|
+
|
152
|
+
def request_fields
|
153
|
+
{ :service_name => @options[:service].name,
|
154
|
+
:method_name => @options[:method].to_s,
|
155
|
+
:request_proto => @options[:request],
|
156
|
+
:caller => request_caller }
|
157
|
+
end
|
158
|
+
|
159
|
+
def send_request
|
160
|
+
fail 'If you inherit a Connector from Base you must implement send_request'
|
161
|
+
end
|
162
|
+
|
163
|
+
def setup_connection
|
164
|
+
initialize_stats
|
165
|
+
@request_data = request_bytes
|
166
|
+
@stats.request_size = @request_data.size
|
167
|
+
end
|
168
|
+
|
169
|
+
def succeed(response)
|
170
|
+
logger.debug { sign_message("Server succeeded request (invoking on_success)") }
|
171
|
+
@success_cb.call(response) unless @success_cb.nil?
|
172
|
+
rescue => e
|
173
|
+
logger.error { sign_message("Success callback error encountered") }
|
174
|
+
log_exception(e)
|
175
|
+
failure(:RPC_ERROR, "An exception occurred while calling on_success: #{e.message}")
|
176
|
+
ensure
|
177
|
+
complete
|
178
|
+
end
|
179
|
+
|
180
|
+
def timeout
|
181
|
+
if options[:timeout]
|
182
|
+
options[:timeout]
|
183
|
+
else
|
184
|
+
300 # seconds
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Wrap the given block in a timeout of the configured number of seconds.
|
189
|
+
#
|
190
|
+
def timeout_wrap(&block)
|
191
|
+
::Timeout.timeout(timeout, &block)
|
192
|
+
rescue ::Timeout::Error
|
193
|
+
failure(:RPC_FAILED, "The server took longer than #{timeout} seconds to respond")
|
194
|
+
end
|
195
|
+
|
196
|
+
def validate_request_type!
|
197
|
+
unless @options[:request].class == @options[:request_type]
|
198
|
+
expected = @options[:request_type].name
|
199
|
+
actual = @options[:request].class.name
|
200
|
+
failure(:INVALID_REQUEST_PROTO, "Expected request type to be type of #{expected}, got #{actual} instead")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def verify_callbacks
|
205
|
+
unless any_callbacks?
|
206
|
+
logger.debug { sign_message("No callbacks set, using data_callback") }
|
207
|
+
@success_cb = @failure_cb = method(:data_callback)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def verify_options!
|
212
|
+
# Verify the options that are necessary and merge them in
|
213
|
+
[:service, :method, :host, :port].each do |opt|
|
214
|
+
failure(:RPC_ERROR, "Invalid client connection configuration. #{opt} must be a defined option.") if @options[opt].nil?
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|