grpc_kit 0.1.0 → 0.1.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +22 -6
  3. data/examples/interceptors/client_logging_interceptor.rb +48 -0
  4. data/examples/interceptors/server_logging_interceptor.rb +44 -0
  5. data/examples/routeguide_client.rb +63 -3
  6. data/examples/routeguide_server.rb +86 -3
  7. data/grpc_kit.gemspec +1 -1
  8. data/lib/grpc.rb +1 -0
  9. data/lib/grpc_kit/client.rb +31 -79
  10. data/lib/grpc_kit/errors.rb +18 -8
  11. data/lib/grpc_kit/grpc/dsl.rb +19 -21
  12. data/lib/grpc_kit/grpc/generic_service.rb +1 -1
  13. data/lib/grpc_kit/grpc/interceptor.rb +57 -0
  14. data/lib/grpc_kit/grpc/logger.rb +17 -0
  15. data/lib/grpc_kit/interceptors.rb +11 -0
  16. data/lib/grpc_kit/interceptors/client_streamer.rb +31 -0
  17. data/lib/grpc_kit/interceptors/request_response.rb +70 -0
  18. data/lib/grpc_kit/interceptors/server_streamer.rb +33 -0
  19. data/lib/grpc_kit/interceptors/streaming.rb +70 -0
  20. data/lib/grpc_kit/method_config.rb +34 -0
  21. data/lib/grpc_kit/protobuffer.rb +20 -0
  22. data/lib/grpc_kit/rpc_desc.rb +103 -27
  23. data/lib/grpc_kit/rpcs.rb +11 -0
  24. data/lib/grpc_kit/rpcs/base.rb +30 -0
  25. data/lib/grpc_kit/rpcs/bidi_streamer.rb +18 -0
  26. data/lib/grpc_kit/rpcs/call.rb +27 -0
  27. data/lib/grpc_kit/rpcs/client_streamer.rb +38 -0
  28. data/lib/grpc_kit/rpcs/request_response.rb +50 -0
  29. data/lib/grpc_kit/rpcs/server_streamer.rb +42 -0
  30. data/lib/grpc_kit/server.rb +19 -38
  31. data/lib/grpc_kit/session/buffer.rb +58 -0
  32. data/lib/grpc_kit/session/client.rb +100 -52
  33. data/lib/grpc_kit/session/duration.rb +96 -0
  34. data/lib/grpc_kit/session/headers.rb +65 -0
  35. data/lib/grpc_kit/session/io.rb +56 -0
  36. data/lib/grpc_kit/session/server.rb +107 -68
  37. data/lib/grpc_kit/session/stream.rb +32 -40
  38. data/lib/grpc_kit/stream.rb +71 -0
  39. data/lib/grpc_kit/streams/client.rb +87 -0
  40. data/lib/grpc_kit/streams/packable.rb +60 -0
  41. data/lib/grpc_kit/streams/send_buffer.rb +48 -0
  42. data/lib/grpc_kit/streams/server.rb +36 -0
  43. data/lib/grpc_kit/streams/stream.rb +58 -0
  44. data/lib/grpc_kit/version.rb +1 -1
  45. metadata +32 -5
  46. data/lib/grpc_kit/io/basic.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f95aa58f7261b6edcb3c31051fbaa164af1ac447f6a12af7d6d962752e0631f9
4
- data.tar.gz: c3520fab3bd5daaff92954af7d665c504dc65dce4131f5d083a35e6cfb45ec4b
3
+ metadata.gz: 278f9b00d1dd37370eae8a829ec68742bcfdd9f693e9189d3abfd0404d2cb31a
4
+ data.tar.gz: 334798bf715bbb1aacab0507457c90a9289c0249c728728e53c7663359318f58
5
5
  SHA512:
6
- metadata.gz: 678ecbed6f6a0e2dc74b2d3d176c2ae83688162d197132f361c7f233bae8dddd3d12c06dee7d87ca0b1d0f0df7f8de07d97b78ac9cf539e270a1de4b86e9e0ae
7
- data.tar.gz: f387757d9332d280ae52ff1d4ca7417731b84a4078d0b9074747bbda42e1ee0b2e5a020c993fcdddcbae4cca76dd5c1ce51c5d44914975dd177b9e7a4cd927ef
6
+ metadata.gz: 25a7c01b82e0726470147c38be66243226483ddf1dafd6b292201cf4c3da99949dc99a2a298e9166f395b531cf936d279a52789730601e32a416378551f2992a
7
+ data.tar.gz: 70fa89ff80eebc5d23bcb0e9b1bec9b3190bba7b2fcb00e7d3eb55f0b1c6ac17a3e9beb4738f69558d740995d45eaccfdd557a44fa02d3ebfdbbf4845d8e9f9c
data/.rubocop.yml CHANGED
@@ -35,7 +35,7 @@ Style/TrailingCommaInArguments:
35
35
  Style/SafeNavigation:
36
36
  Enabled: false
37
37
 
38
- Style/PredicateName:
38
+ Naming/PredicateName:
39
39
  NamePrefixBlacklist:
40
40
  - "is_"
41
41
  - "have_"
@@ -49,13 +49,23 @@ Style/SignalException:
49
49
  Style/SingleLineBlockParams:
50
50
  Enabled: false
51
51
 
52
- Lint/UnderscorePrefixedVariableName:
52
+ Style/NumericLiterals:
53
+ Enabled: false
54
+
55
+ Style/GuardClause:
53
56
  Enabled: false
54
57
 
55
- ##################### Metrics ##################################
58
+ Style/NumericPredicate:
59
+ Enabled: false
60
+
61
+ Metrics/ParameterLists:
62
+ CountKeywordArgs: false
63
+
64
+ Lint/UnderscorePrefixedVariableName:
65
+ Enabled: false
56
66
 
57
67
  Metrics/AbcSize:
58
- Max: 30
68
+ Max: 50
59
69
 
60
70
  Metrics/CyclomaticComplexity:
61
71
  Max: 10
@@ -64,7 +74,13 @@ Metrics/LineLength:
64
74
  Max: 160
65
75
 
66
76
  Metrics/MethodLength:
67
- Max: 20
77
+ Max: 40
68
78
 
69
79
  Metrics/PerceivedComplexity:
70
- Max: 8
80
+ Max: 20
81
+
82
+ Metrics/ClassLength:
83
+ Max: 200
84
+
85
+ Metrics/BlockLength:
86
+ Max: 40
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit'
4
+
5
+ class LoggingInterceptor < GRPC::ClientInterceptor
6
+ def request_response(request: nil, method: nil, **)
7
+ now = Time.now.to_i
8
+ GrpcKit.logger.info("Started request #{request}, method=#{method.name}, service_name=#{method.receiver.class.service_name}")
9
+ yield.tap do
10
+ GrpcKit.logger.info("Elapsed Time: #{Time.now.to_i - now}")
11
+ end
12
+ end
13
+
14
+ def client_streamer(call: nil, method: nil, **)
15
+ GrpcKit.logger.info("Started request method=#{method.name}, service_name=#{method.receiver.class.service_name}")
16
+ yield(LoggingStream.new(call))
17
+ end
18
+
19
+ def server_streamer(call: nil, method: nil, **)
20
+ GrpcKit.logger.info("Started request method=#{method.name}, service_name=#{method.receiver.class.service_name}")
21
+ yield(LoggingStream.new(call))
22
+ end
23
+
24
+ def bidi_streamer(**)
25
+ yield
26
+ end
27
+
28
+ class LoggingStream
29
+ def initialize(stream)
30
+ @stream = stream
31
+ end
32
+
33
+ def send_msg(msg, **opts)
34
+ GrpcKit.logger.info("logging interceptor send #{msg}")
35
+ @stream.send_msg(msg, opts)
36
+ end
37
+
38
+ def recv(**opt)
39
+ @stream.recv(opt).tap do |v|
40
+ GrpcKit.logger.info("logging interceptor recv #{v}")
41
+ end
42
+ end
43
+
44
+ def close_and_recv
45
+ @stream.close_and_recv
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit'
4
+
5
+ class LoggingInterceptor < GRPC::ServerInterceptor
6
+ def request_response(request: nil, call: nil, method: nil)
7
+ now = Time.now.to_i
8
+ GrpcKit.logger.info("Started request #{request}, method=#{method.name}, service_name=#{method.receiver.class.service_name}")
9
+ yield(request, call).tap do
10
+ GrpcKit.logger.info("Elapsed Time: #{Time.now.to_i - now}")
11
+ end
12
+ end
13
+
14
+ def client_streamer(call: nil, method: nil)
15
+ GrpcKit.logger.info("Started request method=#{method.name}, service_name=#{method.receiver.class.service_name}")
16
+ yield(LoggingStream.new(call))
17
+ end
18
+
19
+ def server_streamer(call: nil, method: nil, **)
20
+ GrpcKit.logger.info("Started request method=#{method.name}, service_name=#{method.receiver.class.service_name}")
21
+ yield(LoggingStream.new(call))
22
+ end
23
+
24
+ def bidi_streamer(**)
25
+ yield
26
+ end
27
+
28
+ class LoggingStream
29
+ def initialize(stream)
30
+ @stream = stream
31
+ end
32
+
33
+ def send_msg(msg, **opt)
34
+ GrpcKit.logger.info("logging interceptor send #{msg}")
35
+ @stream.send_msg(msg, opt)
36
+ end
37
+
38
+ def recv(**opt)
39
+ @stream.recv(**opt).tap do |v|
40
+ GrpcKit.logger.info("logging interceptor recv #{v}")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,23 +1,83 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Style/GlobalVars
4
+
3
5
  $LOAD_PATH.unshift File.expand_path('./examples/routeguide')
4
6
 
5
7
  require 'grpc_kit'
6
8
  require 'pry'
9
+ require 'json'
10
+ require 'logger'
7
11
  require 'routeguide_services_pb'
8
12
 
9
- stub = Routeguide::RouteGuide::Stub.new('localhost', 50051)
13
+ RESOURCE_PATH = './examples/routeguide/routeguide.json'
14
+
15
+ $logger = Logger.new(STDOUT)
10
16
 
11
17
  def get_feature(stub)
18
+ $logger.info('===== get_feature =====')
12
19
  points = [
13
20
  Routeguide::Point.new(latitude: 409_146_138, longitude: -746_188_906),
14
21
  Routeguide::Point.new(latitude: 0, longitude: 0)
15
22
  ]
16
23
 
17
24
  points.each do |pt|
18
- feature = stub.get_feature(pt)
19
- puts "get_feature #{feature.name}, #{feature.location}"
25
+ feature = stub.get_feature(pt, metadata: { 'metadata' => 'data1' })
26
+ if feature.name == ''
27
+ $logger.info("Found nothing at #{feature.inspect}")
28
+ else
29
+ $logger.info("Found '#{feature.name}' at #{feature.location.inspect}")
30
+ end
31
+ end
32
+ end
33
+
34
+ def list_features(stub)
35
+ $logger.info('===== list_features =====')
36
+ rect = Routeguide::Rectangle.new(
37
+ lo: Routeguide::Point.new(latitude: 400_000_000, longitude: -750_000_000),
38
+ hi: Routeguide::Point.new(latitude: 420_000_000, longitude: -730_000_000),
39
+ )
40
+
41
+ stream = stub.list_features(rect)
42
+
43
+ loop do
44
+ r = stream.recv
45
+ $logger.info("Found #{r.name} at #{r.location.inspect}")
20
46
  end
21
47
  end
22
48
 
49
+ def record_route(stub, size)
50
+ features = File.open(RESOURCE_PATH) do |f|
51
+ JSON.parse(f.read)
52
+ end
53
+
54
+ stream = stub.record_route({})
55
+
56
+ size.times do
57
+ location = features.sample['location']
58
+ point = Routeguide::Point.new(latitude: location['latitude'], longitude: location['longitude'])
59
+ puts "Next point is #{point.inspect}"
60
+ stream.send_msg(point)
61
+ sleep(rand(0..1))
62
+ end
63
+
64
+ resp = stream.close_and_recv
65
+ puts "summary: #{resp[0].inspect}"
66
+ end
67
+
68
+ opts = {}
69
+
70
+ if ENV['GRPC_INTERCEPTOR']
71
+ require_relative 'interceptors/client_logging_interceptor'
72
+ opts[:interceptors] = [LoggingInterceptor.new]
73
+ elsif ENV['GRPC_TIMEOUT']
74
+ opts[:timeout] = Integer(ENV['GRPC_TIMEOUT'])
75
+ end
76
+
77
+ stub = Routeguide::RouteGuide::Stub.new('localhost', 50051, **opts)
78
+
23
79
  get_feature(stub)
80
+ list_features(stub)
81
+ record_route(stub, 10)
82
+
83
+ # rubocop:enable Style/GlobalVars
@@ -6,26 +6,109 @@ require 'grpc_kit'
6
6
  require 'pry'
7
7
  require 'json'
8
8
  require 'routeguide_services_pb'
9
+ require 'logger'
9
10
 
10
11
  class Server < Routeguide::RouteGuide::Service
11
12
  RESOURCE_PATH = './examples/routeguide/routeguide.json'
12
13
 
13
14
  def initialize
15
+ @logger = Logger.new(STDOUT)
14
16
  File.open(RESOURCE_PATH) do |f|
15
- features = JSON.load(f.read)
17
+ features = JSON.parse(f.read)
16
18
  @features = Hash[features.map { |x| [x['location'], x['name']] }]
17
19
  end
18
20
  end
19
21
 
20
- def get_feature(point, _call)
22
+ def get_feature(point, ctx)
21
23
  name = @features.fetch({ 'longitude' => point.longitude, 'latitude' => point.latitude }, '')
24
+ @logger.info("Point longitude=#{point.longitude}, latitude=#{point.latitude}, metadata=#{ctx.metadata}")
22
25
  Routeguide::Feature.new(location: point, name: name)
23
26
  end
27
+
28
+ def list_features(rect, stream)
29
+ @logger.info('===== list_features =====')
30
+
31
+ @features.each do |location, name|
32
+ if name.nil? || name == '' || !in_range(location, rect)
33
+ next
34
+ end
35
+
36
+ pt = Routeguide::Point.new(location)
37
+ resp = Routeguide::Feature.new(location: pt, name: name)
38
+ @logger.info(resp)
39
+ stream.send_msg(resp)
40
+ end
41
+ end
42
+
43
+ def record_route(stream)
44
+ @logger.info('===== record_route =====')
45
+ distance = 0
46
+ count = 0
47
+ features = 0
48
+ start_at = Time.now.to_i
49
+ last = nil
50
+
51
+ loop do
52
+ point = stream.recv # XXX: raise StopIteration
53
+ @logger.info(point)
54
+
55
+ count += 1
56
+ name = @features.fetch({ 'longitude' => point.longitude, 'latitude' => point.latitude }, '')
57
+ unless name == ''
58
+ features += 1
59
+ end
60
+
61
+ last = point
62
+ distance += calculate_distance(point, last)
63
+ end
64
+
65
+ Routeguide::RouteSummary.new(
66
+ point_count: count,
67
+ feature_count: features,
68
+ distance: distance,
69
+ elapsed_time: Time.now.to_i - start_at,
70
+ )
71
+ end
72
+
73
+ private
74
+
75
+ COORD_FACTOR = 1e7
76
+ RADIUS = 637_100
77
+
78
+ def calculate_distance(point_a, point_b)
79
+ lat_a = (point_a.latitude / COORD_FACTOR) * Math::PI / 180
80
+ lat_b = (point_b.latitude / COORD_FACTOR) * Math::PI / 180
81
+ lon_a = (point_a.longitude / COORD_FACTOR) * Math::PI / 180
82
+ lon_b = (point_b.longitude / COORD_FACTOR) * Math::PI / 180
83
+
84
+ delta_lat = lat_a - lat_b
85
+ delta_lon = lon_a - lon_b
86
+ a = Math.sin(delta_lat / 2)**2 + Math.cos(lat_a) * Math.cos(lat_b) + Math.sin(delta_lon / 2)**2
87
+ (2 * RADIUS * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))).to_i
88
+ end
89
+
90
+ def in_range(point, rect)
91
+ longitudes = [rect.lo.longitude, rect.hi.longitude]
92
+ left = longitudes.min
93
+ right = longitudes.max
94
+
95
+ latitudes = [rect.lo.latitude, rect.hi.latitude]
96
+ bottom = latitudes.min
97
+ top = latitudes.max
98
+ (point['longitude'] >= left) && (point['longitude'] <= right) && (point['latitude'] >= bottom) && (point['latitude'] <= top)
99
+ end
24
100
  end
25
101
 
26
102
  sock = TCPServer.new(50051)
27
103
 
28
- server = GrpcKit::Server.new
104
+ opts = {}
105
+
106
+ if ENV['GRPC_INTERCEPTOR']
107
+ require_relative 'interceptors/server_logging_interceptor'
108
+ opts[:interceptors] = [LoggingInterceptor.new]
109
+ end
110
+
111
+ server = GrpcKit::Server.new(**opts)
29
112
  server.handle(Server.new)
30
113
  server.run
31
114
 
data/grpc_kit.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
- spec.add_dependency 'ds9', '~> 1.1.1'
25
+ spec.add_dependency 'ds9', '~> 1.2.0'
26
26
  spec.add_dependency 'google-protobuf', '~> 3.6.1'
27
27
  spec.add_dependency 'googleapis-common-protos-types', '~> 1.0.2'
28
28
 
data/lib/grpc.rb CHANGED
@@ -3,5 +3,6 @@
3
3
  # Compat file for xx_services_pb.rb
4
4
  require 'grpc_kit'
5
5
  require 'grpc_kit/grpc/generic_service'
6
+ require 'grpc_kit/grpc/interceptor'
6
7
 
7
8
  GRPC = GrpcKit::GRPC
@@ -2,106 +2,58 @@
2
2
 
3
3
  require 'socket'
4
4
  require 'grpc_kit/session/client'
5
+ require 'grpc_kit/session/duration'
6
+ require 'grpc_kit/session/io'
7
+ require 'grpc_kit/rpcs'
5
8
 
6
9
  module GrpcKit
7
10
  class Client
8
- def initialize(host, port, io = GrpcKit::IO::Basic)
11
+ def initialize(host, port, interceptors: [], timeout: nil)
9
12
  @host = host
10
13
  @port = port
11
14
  @authority = "#{host}:#{port}"
12
- @io = io
13
- end
14
-
15
- # @params handler [object]
16
- def handle(handler)
17
- klass = handler.class
18
-
19
- klass.rpc_descs.values.each do |rpc_desc|
20
- path = rpc_desc.path(klass.service_name)
21
- if @rpc_descs[path]
22
- raise "Duplicated method registered #{key}, class: #{handler}"
23
- end
24
-
25
- @rpc_descs[path] = [rpc_desc, handler]
26
- end
27
- end
28
-
29
- class RequestResponse
30
- attr_writer :session
31
-
32
- def initialize(path, opts = {})
33
- @path = path
34
- @opts = opts
35
- @session = nil
36
- @data = ''
37
- end
38
-
39
- def invoke(data)
40
- @session.submit_settings([])
41
-
42
- stream_id = submit_request(data)
43
- @session.start(stream_id)
44
- @data
45
- end
46
-
47
- def on_data_chunk_recv(stream, data)
48
- compressed, length, buf = data.unpack('CNa*')
49
- if compressed == 0 # TODO: not
50
- if length != buf.size
51
- raise 'recived data inconsistent'
52
- end
53
-
54
- @data << buf
55
- stream.recv2(buf)
15
+ @interceptors = interceptors
16
+ @timeout =
17
+ if timeout
18
+ GrpcKit::Session::Duration.from_numeric(timeout)
56
19
  else
57
- raise 'not supported'
20
+ nil
58
21
  end
59
- end
60
-
61
- private
62
-
63
- def submit_request(data)
64
- @session.submit_request(
65
- {
66
- ':method' => 'POST',
67
- ':scheme' => 'http',
68
- ':authority' => @opts[:authority],
69
- ':path' => @path.to_s,
70
- 'te' => 'trailers',
71
- 'content-type' => 'application/grpc',
72
- 'user-agent' => "grpc-ruby/#{GrpcKit::VERSION} (grpc_kit)",
73
- 'grpc-accept-encoding' => 'identity,deflate,gzip',
74
- },
75
- data,
76
- )
77
- end
78
22
  end
79
23
 
80
- def request_response(path, request, rpc_desc, opts = {})
24
+ def request_response(rpc, request, opts = {})
81
25
  GrpcKit.logger.info('Calling request_respose')
82
- sock = TCPSocket.new(@host, @port)
83
-
84
- rr = RequestResponse.new(path, { authority: @authority }.merge(opts))
85
- session = GrpcKit::Session::Client.new(@io.new(sock, sock), rr)
86
- rr.session = session
87
26
 
88
- req = rpc_desc.encode2(request)
89
- data = [0, req.length, req].pack('CNa*')
90
-
91
- resp = rr.invoke(data)
92
- rpc_desc.decode2(resp)
27
+ rpc.config.interceptor.interceptors = @interceptors
28
+ do_request(rpc, request, opts)
93
29
  end
94
30
 
95
- def client_streamer(path, requests, rpc, opts = {})
31
+ def client_streamer(rpc, opts = {})
96
32
  GrpcKit.logger.info('Calling client_streamer')
33
+ rpc.config.interceptor.interceptors = @interceptors
34
+ do_request(rpc, nil, opts)
97
35
  end
98
36
 
99
- def server_streamer(path, request, metadata, opts = {})
37
+ def server_streamer(rpc, request, opts = {})
100
38
  GrpcKit.logger.info('Calling server_streamer')
39
+ rpc.config.interceptor.interceptors = @interceptors
40
+ do_request(rpc, request, opts)
101
41
  end
102
42
 
103
- def bidi_streamer(path, requests, metadata, opts = {})
43
+ def bidi_streamer(rpc, requests, opts = {})
44
+ rpc.config.interceptor.interceptors = @interceptors
104
45
  GrpcKit.logger.info('Calling bidi_streamer')
105
46
  end
47
+
48
+ private
49
+
50
+ def do_request(rpc, request, **opts)
51
+ sock = TCPSocket.new(@host, @port) # XXX
52
+ session = GrpcKit::Session::Client.new(GrpcKit::Session::IO.new(sock), rpc)
53
+ session.submit_settings([])
54
+
55
+ default = { timeout: @timeout, authority: @authority }.compact
56
+ rpc.invoke(session, request, opts.merge(default))
57
+ end
106
58
  end
107
59
  end