grpc_kit 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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