grpc_kit 0.1.4 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7415d182781136999b2e1630ef9dff77a61196225b4207e9d9535a2b566fceee
4
- data.tar.gz: 9836c54ed198740c4a75e90b957a641528b06333a30314c03441b9dce35f8731
3
+ metadata.gz: 5cd6355c75605e749f1590fa3f3f3346f5d77288753df21cd104c9af78d22f5f
4
+ data.tar.gz: 3e6d0c8606b0476edaca281b510b1c8d1f047df7accc479e2b1e569567869497
5
5
  SHA512:
6
- metadata.gz: '083b685dd4f7966156a1ac41eab594ace1664c813105e1204c521ce85e727945170ef1c701097ee6f93467407b13d08ea649d6da5f86af54ebd60ca1e373336e'
7
- data.tar.gz: 505029bdcbfb035cb41b10ed845cec6ce27eae2ff350a27f49cb8a3b2cb4afeca92b8e5b9a7bc70e7dc0e2e8bde9d88591cc8899a871d81d2665c5c8141a1d14
6
+ metadata.gz: bde53f9753f23d273ed4b211c63f2e4727e88c8d67b9b330add41c9bec5b82ebbe884c551c496c5fcdca57252e36d58660ac5a70f31ecd8cb84f00258a6972f9
7
+ data.tar.gz: 75d4b9b74c7cc99111c348c754c79cda00295745485ee6d28a4e0aa492bc28bcc6303dd04bbd95320a483a3bc8559840c79be09621eabb04bfb853b7e7be9760
data/README.md CHANGED
@@ -47,7 +47,8 @@ end
47
47
  ##### Client
48
48
 
49
49
  ```ruby
50
- stub = Helloworld::Greeter::Stub.new('localhost', 50051)
50
+ sock = TCPSocket.new('localhost', 50051)
51
+ stub = Helloworld::Greeter::Stub.new(sock)
51
52
  message = stub.say_hello(Helloworld::HelloRequest.new(name: 'your name')).message
52
53
  puts message
53
54
  ```
data/TODO.md CHANGED
@@ -13,8 +13,8 @@
13
13
  ### server streamer
14
14
 
15
15
  - [x] recv/send msg
16
- - [ ] metadata (client)
17
- - [ ] metadata (server)
16
+ - [x] metadata (client)
17
+ - [x] metadata (server)
18
18
  - [x] interceptor (client)
19
19
  - [x] interceptor (server)
20
20
  - [ ] deadline (client)
@@ -23,8 +23,8 @@
23
23
  ### client streamer
24
24
 
25
25
  - [x] recv/send msg
26
- - [ ] metadata (client)
27
- - [ ] metadata (server)
26
+ - [x] metadata (client)
27
+ - [x] metadata (server)
28
28
  - [x] interceptor (client)
29
29
  - [x] interceptor (server)
30
30
  - [ ] deadline (client)
@@ -62,3 +62,10 @@
62
62
  - [ ] add server request spec
63
63
  - [ ] add client request spec
64
64
  - [ ] handle RST FRAME
65
+
66
+ ## bugs
67
+
68
+ - [x] status_check is invoked twice
69
+ - [x] undefined local variable or method `finish' for #<GrpcKit::Sessions::ClientSession:0x00007f9ae3abf970> (NameError)
70
+ - [x] clients don't use same object even if thier connections alive
71
+
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.2.0'
25
+ spec.add_dependency 'ds9', '~> 1.2.1'
26
26
  spec.add_dependency 'google-protobuf', '~> 3.6.1'
27
27
  spec.add_dependency 'googleapis-common-protos-types', '~> 1.0.2'
28
28
 
@@ -9,10 +9,10 @@ module GrpcKit
9
9
  attr_reader :metadata
10
10
  alias outgoing_metadata metadata
11
11
 
12
- def send_msg(data, timeout: nil, metadata: {}, last: false)
12
+ def send_msg(data, timeout: nil, last: false)
13
13
  raise 'No method error' if @restrict
14
14
 
15
- @stream.send_msg(data, last: last, timeout: timeout, metadata: metadata)
15
+ @stream.send_msg(data, last: last, timeout: timeout, metadata: outgoing_metadata)
16
16
  end
17
17
 
18
18
  def recv(last: false)
@@ -9,10 +9,10 @@ module GrpcKit
9
9
  attr_reader :metadata
10
10
  alias outgoing_metadata metadata
11
11
 
12
- def send_msg(data, timeout: nil, metadata: {}, last: false)
12
+ def send_msg(data, timeout: nil, last: false)
13
13
  raise 'No method error' if @restrict
14
14
 
15
- @stream.send_msg(data, last: last, timeout: timeout, metadata: metadata)
15
+ @stream.send_msg(data, last: last, timeout: timeout, metadata: outgoing_metadata)
16
16
  end
17
17
 
18
18
  def recv(last: false)
@@ -9,10 +9,10 @@ module GrpcKit
9
9
  attr_reader :metadata
10
10
  alias outgoing_metadata metadata
11
11
 
12
- def send_msg(data, timeout: nil, metadata: {}, last: false)
12
+ def send_msg(data, timeout: nil, last: false)
13
13
  raise 'No method error' if @restrict
14
14
 
15
- @stream.send_msg(data, last: last, timeout: timeout, metadata: metadata)
15
+ @stream.send_msg(data, last: last, timeout: timeout, metadata: outgoing_metadata)
16
16
  end
17
17
 
18
18
  def recv(last: false)
@@ -19,7 +19,14 @@ module GrpcKit
19
19
  def send_msg(data, last: false)
20
20
  raise 'No method error' if @restrict
21
21
 
22
- @stream.send_msg(data, @protobuf, last: last, limit_size: @config.max_send_message_size)
22
+ @stream.send_msg(
23
+ data,
24
+ @protobuf,
25
+ last: last,
26
+ initial_metadata: @outgoing_initial_metadata,
27
+ trailing_metadata: @outgoing_trailing_metadata,
28
+ limit_size: @config.max_send_message_size,
29
+ )
23
30
  end
24
31
 
25
32
  def recv(last: false)
@@ -19,7 +19,14 @@ module GrpcKit
19
19
  def send_msg(data, last: false)
20
20
  raise 'No method error' if @restrict
21
21
 
22
- @stream.send_msg(data, @protobuf, last: last, limit_size: @config.max_send_message_size)
22
+ @stream.send_msg(
23
+ data,
24
+ @protobuf,
25
+ last: last,
26
+ initial_metadata: @outgoing_initial_metadata,
27
+ trailing_metadata: @outgoing_trailing_metadata,
28
+ limit_size: @config.max_send_message_size,
29
+ )
23
30
  end
24
31
 
25
32
  def recv(last: false)
@@ -19,7 +19,14 @@ module GrpcKit
19
19
  def send_msg(data, last: false)
20
20
  raise 'No method error' if @restrict
21
21
 
22
- @stream.send_msg(data, @protobuf, last: last, limit_size: @config.max_send_message_size)
22
+ @stream.send_msg(
23
+ data,
24
+ @protobuf,
25
+ last: last,
26
+ initial_metadata: @outgoing_initial_metadata,
27
+ trailing_metadata: @outgoing_trailing_metadata,
28
+ limit_size: @config.max_send_message_size,
29
+ )
23
30
  end
24
31
 
25
32
  def recv(last: false)
@@ -8,6 +8,9 @@ module GrpcKit
8
8
  Klass = Struct.new(:service_name)
9
9
  attr_reader :method
10
10
 
11
+ # @params stream [GrpcKit::Stream::ServerStream|GrpcKit::Stream::ClientStream]
12
+ # @params config [GrpcKit::MethodConfig]
13
+ # @params metadata [Hash]
11
14
  def initialize(stream:, config:, metadata:, timeout: nil)
12
15
  @config = config
13
16
  @metadata = metadata
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'grpc_kit/grpc_time'
4
4
  require 'grpc_kit/session/io'
5
- require 'grpc_kit/sessions/client_session'
6
- require 'grpc_kit/streams/client_stream'
7
- require 'grpc_kit/transports/client_transport'
5
+ require 'grpc_kit/session/client_session'
6
+ require 'grpc_kit/stream/client_stream'
7
+ require 'grpc_kit/transport/client_transport'
8
8
 
9
9
  module GrpcKit
10
10
  class Client
@@ -17,43 +17,46 @@ module GrpcKit
17
17
  addr = sock.addr
18
18
  "#{addr[3]}:#{addr[1]}"
19
19
  end
20
- @interceptors = interceptors
20
+
21
21
  @timeout = timeout && GrpcKit::GrpcTime.new(timeout)
22
+
23
+ build_rpcs(interceptors)
22
24
  end
23
25
 
24
26
  def request_response(rpc, request, opts = {})
25
27
  GrpcKit.logger.debug('Calling request_respose')
26
-
27
- rpc.config.interceptor.interceptors = @interceptors
28
28
  do_request(rpc, request, opts)
29
29
  end
30
30
 
31
31
  def client_streamer(rpc, opts = {})
32
32
  GrpcKit.logger.debug('Calling client_streamer')
33
- rpc.config.interceptor.interceptors = @interceptors
34
33
  do_request(rpc, nil, opts)
35
34
  end
36
35
 
37
36
  def server_streamer(rpc, request, opts = {})
38
37
  GrpcKit.logger.debug('Calling server_streamer')
39
- rpc.config.interceptor.interceptors = @interceptors
40
38
  do_request(rpc, request, opts)
41
39
  end
42
40
 
43
41
  def bidi_streamer(rpc, requests, opts = {})
44
- rpc.config.interceptor.interceptors = @interceptors
45
42
  GrpcKit.logger.debug('Calling bidi_streamer')
46
43
  end
47
44
 
48
45
  private
49
46
 
50
47
  def do_request(rpc, request, **opts)
51
- session = GrpcKit::Sessions::ClientSession.new(GrpcKit::Session::IO.new(@sock))
52
- session.submit_settings([])
53
-
54
- t = GrpcKit::Transports::ClientTransport.new(session)
55
- cs = GrpcKit::Streams::ClientStream.new(t, rpc.config, authority: @authority, timeout: @timeout)
48
+ t = GrpcKit::Transport::ClientTransport.new(session)
49
+ cs = GrpcKit::Stream::ClientStream.new(t, rpc.config, authority: @authority, timeout: @timeout)
56
50
  rpc.invoke(cs, request, opts.merge(timeout: @timeout))
57
51
  end
52
+
53
+ def session
54
+ @session ||=
55
+ begin
56
+ s = GrpcKit::Session::ClientSession.new(GrpcKit::Session::IO.new(@sock))
57
+ s.submit_settings([])
58
+ s
59
+ end
60
+ end
58
61
  end
59
62
  end
@@ -48,27 +48,40 @@ module GrpcKit
48
48
  end
49
49
 
50
50
  def rpc_stub_class
51
- rpc_descs_ = rpc_descs
51
+ rpc_descs_ = {}
52
+ rpc_descs.each_value do |rpc_desc|
53
+ rpc_descs_[rpc_desc.ruby_style_name] = rpc_desc
54
+ end
55
+
52
56
  Class.new(GrpcKit::Client) do
53
- rpc_descs_.each do |_, rpc_desc|
54
- method_name = rpc_desc.ruby_style_name
55
- rpc = rpc_desc.build_client
57
+ def initialize(*)
58
+ @rpcs = {}
59
+ super
60
+ end
61
+
62
+ define_method(:build_rpcs) do |interceptors|
63
+ rpc_descs_.each do |method_name, rpc_desc|
64
+ @rpcs[method_name] = rpc_desc.build_client(interceptors: interceptors)
65
+ end
66
+ end
67
+ private :build_rpcs
56
68
 
69
+ rpc_descs_.each do |method_name, rpc_desc|
57
70
  if rpc_desc.request_response?
58
71
  define_method(method_name) do |request, opts = {}|
59
- request_response(rpc, request, opts)
72
+ request_response(@rpcs.fetch(method_name), request, opts)
60
73
  end
61
74
  elsif rpc_desc.client_streamer?
62
75
  define_method(method_name) do |opts = {}|
63
- client_streamer(rpc, opts)
76
+ client_streamer(@rpcs.fetch(method_name), opts)
64
77
  end
65
78
  elsif rpc_desc.server_streamer?
66
79
  define_method(method_name) do |request, opts = {}|
67
- server_streamer(rpc, request, opts)
80
+ server_streamer(@rpcs.fetch(method_name), request, opts)
68
81
  end
69
82
  elsif rpc_desc.bidi_streamer?
70
83
  define_method(method_name) do |requests, opts = {}, &blk|
71
- bidi_streamer(rpc, requests, opts, &blk)
84
+ bidi_streamer(@rpcs.fetch(method_name), requests, opts, &blk)
72
85
  end
73
86
  else
74
87
  raise "unknown #{rpc_desc}"
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GrpcKit
4
4
  class GrpcTime
5
- MAX = 10**9-1
5
+ MAX = 10**9 - 1
6
6
 
7
7
  # @params value [String|Integer]
8
8
  def initialize(value)
@@ -67,12 +67,7 @@ module GrpcKit
67
67
  private
68
68
 
69
69
  def from_integer(value)
70
- if value < 0
71
- @value = MAX
72
- else
73
- @value = value
74
- end
75
-
70
+ @value = value < 0 ? MAX : value
76
71
  @unit = 'S'
77
72
  end
78
73
 
@@ -5,11 +5,8 @@ require 'grpc_kit/interceptors'
5
5
  module GrpcKit
6
6
  module Interceptors::Client
7
7
  class RequestResponse
8
- attr_writer :interceptors
9
-
10
- def initialize
11
- # Cant' get interceptor at definition time...
12
- @interceptors = nil
8
+ def initialize(interceptors)
9
+ @interceptors = interceptors
13
10
  end
14
11
 
15
12
  def intercept(request, call, metadata, &block)
@@ -4,11 +4,8 @@ module GrpcKit
4
4
  module Interceptors
5
5
  module Client
6
6
  class Streaming
7
- attr_writer :interceptors
8
-
9
- def initialize
10
- # Cant' get interceptor at definition time...
11
- @interceptors = nil
7
+ def initialize(interceptors)
8
+ @interceptors = interceptors
12
9
  end
13
10
 
14
11
  def intercept(call, metadata, &block)
@@ -46,14 +46,16 @@ module GrpcKit
46
46
  server.new(handler, config)
47
47
  end
48
48
 
49
- def build_client
49
+ def build_client(interceptors: [])
50
+ inter = interceptors.empty? ? nil : client_interceptor.new(interceptors)
51
+
50
52
  config = GrpcKit::MethodConfig.build_for_client(
51
53
  path: path,
52
54
  ruby_style_method_name: ruby_style_name,
53
55
  protobuf: client_protobuf,
54
56
  service_name: @server_name,
55
57
  method_name: @name,
56
- interceptor: client_interceptor.new,
58
+ interceptor: inter,
57
59
  )
58
60
  client.new(config)
59
61
  end
@@ -14,8 +14,10 @@ module GrpcKit
14
14
  stream: stream,
15
15
  )
16
16
 
17
- @config.interceptor.intercept(call, metadata) do |s|
18
- s
17
+ if @config.interceptor
18
+ @config.interceptor.intercept(call, metadata) { |s| s }
19
+ else
20
+ call
19
21
  end
20
22
  end
21
23
  end
@@ -14,16 +14,27 @@ module GrpcKit
14
14
  stream: stream,
15
15
  )
16
16
 
17
- @config.interceptor.intercept(request, call, call.metadata) do |r, c, _|
18
- if timeout
17
+ # TODO: DRY
18
+ if @config.interceptor && timeout
19
+ @config.interceptor.intercept(request, call, call.metadata) do |r, c, _|
19
20
  Timeout.timeout(timeout.to_f, GrpcKit::Errors::DeadlineExceeded) do
20
- call.send_msg(r, timeout: timeout.to_s, metadata: c.metadata, last: true)
21
+ call.send_msg(request, timeout: timeout.to_s, last: true)
21
22
  call.recv(last: true)
22
23
  end
23
- else
24
- call.send_msg(r, metadata: c.metadata, last: true)
24
+ end
25
+ elsif @config.interceptor && !timeout
26
+ @config.interceptor.intercept(request, call, call.metadata) do |r, c, _|
27
+ call.send_msg(request, last: true)
28
+ call.recv(last: true)
29
+ end
30
+ elsif !@config.interceptor && timeout
31
+ Timeout.timeout(timeout.to_f, GrpcKit::Errors::DeadlineExceeded) do
32
+ call.send_msg(request, timeout: timeout.to_s, last: true)
25
33
  call.recv(last: true)
26
34
  end
35
+ else
36
+ call.send_msg(request, last: true)
37
+ call.recv(last: true)
27
38
  end
28
39
  end
29
40
  end
@@ -9,9 +9,14 @@ module GrpcKit
9
9
  def invoke(stream, request, metadata: {}, timeout: nil)
10
10
  call = GrpcKit::Calls::Client::ServerStreamer.new(metadata: metadata, config: @config, timeout: timeout, stream: stream)
11
11
 
12
- @config.interceptor.intercept(call, metadata) do |c, m|
13
- c.send_msg(request, metadata: m, last: true)
14
- c
12
+ if @config.interceptor
13
+ @config.interceptor.intercept(call, metadata) do |c, m|
14
+ c.send_msg(request, last: true)
15
+ c
16
+ end
17
+ else
18
+ call.send_msg(request, last: true)
19
+ call
15
20
  end
16
21
  end
17
22
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'grpc_kit/session/io'
4
- require 'grpc_kit/sessions/server_session'
4
+ require 'grpc_kit/session/server_session'
5
5
 
6
6
  module GrpcKit
7
7
  class Server
@@ -10,6 +10,8 @@ module GrpcKit
10
10
  @rpc_descs = {}
11
11
  @interceptors = interceptors
12
12
  @mutex = Mutex.new
13
+ @stopping = false
14
+ @max_graceful_wait = 60
13
15
 
14
16
  GrpcKit.logger.debug("Launched grpc_kit(v#{GrpcKit::VERSION})")
15
17
  end
@@ -26,23 +28,51 @@ module GrpcKit
26
28
  end
27
29
 
28
30
  def run(conn)
31
+ raise 'Stopping server' if @stopping
32
+
29
33
  establish_session(conn) do |s|
30
34
  s.submit_settings([])
31
35
  s.start
32
36
  end
33
37
  end
34
38
 
35
- def shutdown
36
- GrpcKit.logger.debug('Shutdown grpc_kit')
39
+ def wait_shutdown
40
+ loop do
41
+ return if @stopping
42
+
43
+ sleep 1
44
+ end
45
+ end
37
46
 
38
- @mutex.synchronize do
39
- @sessions.each(&:finish)
47
+ def force_shutdown
48
+ # expected to be called in trap context
49
+ Thread.new do
50
+ @mutex.synchronize do
51
+ GrpcKit.logger.debug('force shutdown')
52
+ @stopping = true
53
+ @sessions.each(&:shutdown)
54
+ end
40
55
  end
41
56
  end
42
57
 
43
58
  def graceful_shutdown
44
- @mutex.synchronize do
45
- @sessions.each(&:drain)
59
+ # expected to be called in trap context
60
+ Thread.new do
61
+ GrpcKit.logger.debug('graceful shutdown')
62
+ @mutex.synchronize { @sessions.each(&:drain) }
63
+ begin
64
+ Timeout.timeout(@max_graceful_wait) do
65
+ loop do
66
+ break if @mutex.synchronize { @sessions.empty? }
67
+
68
+ sleep 1
69
+ end
70
+ end
71
+ rescue Timeout::Error => _
72
+ GrpcKit.logger.debug('Max wait time expired')
73
+ end
74
+
75
+ @stopping = true
46
76
  end
47
77
  end
48
78
 
@@ -62,7 +92,7 @@ module GrpcKit
62
92
  private
63
93
 
64
94
  def establish_session(conn)
65
- session = GrpcKit::Sessions::ServerSession.new(GrpcKit::Session::IO.new(conn), self)
95
+ session = GrpcKit::Session::ServerSession.new(GrpcKit::Session::IO.new(conn), self)
66
96
  begin
67
97
  @mutex.synchronize { @sessions << session }
68
98
  yield(session)
@@ -5,10 +5,14 @@ require 'forwardable'
5
5
  require 'grpc_kit/session/stream'
6
6
 
7
7
  module GrpcKit
8
- module Sessions
8
+ module Session
9
9
  class ClientSession < DS9::Client
10
+ class ConnectionClosing < StandardError; end
11
+
10
12
  extend Forwardable
11
13
 
14
+ MAX_STREAM_ID = 2**31 - 1
15
+
12
16
  delegate %i[send_event recv_event] => :@io
13
17
 
14
18
  # @params io [GrpcKit::Session::IO]
@@ -18,16 +22,24 @@ module GrpcKit
18
22
  @io = io
19
23
  @streams = {}
20
24
  @opts = opts
25
+ @draining = false
26
+ @stop = false
27
+ @last_stream_id = 0
21
28
  end
22
29
 
23
30
  def send_request(data, headers)
24
- stream_id = submit_request(headers, data)
31
+ if @draining
32
+ raise ConnectionClosing, "You can't send new request. becuase this connection will shuting down"
33
+ end
34
+
35
+ stream_id = submit_request(headers, data).to_i
25
36
  stream = GrpcKit::Session::Stream.new(stream_id: stream_id, send_data: data)
26
37
  stream.stream_id = stream_id
27
38
  @streams[stream_id] = stream
28
39
  stream
29
40
  end
30
41
 
42
+ # @params stream_id [Integer]
31
43
  def start(stream_id)
32
44
  stream = @streams.fetch(stream_id)
33
45
 
@@ -38,13 +50,18 @@ module GrpcKit
38
50
 
39
51
  run_once
40
52
  end
41
- rescue Errno::ECONNRESET, IOError
42
- finish
53
+ rescue Errno::ECONNRESET, IOError => e
54
+ GrpcKit.logger.debug(e.message)
55
+ shutdown
43
56
  end
44
57
 
45
58
  def run_once
46
59
  return if @stop
47
60
 
61
+ if @draining && @drain_time < Time.now
62
+ raise 'trasport is closing'
63
+ end
64
+
48
65
  if want_read?
49
66
  do_read
50
67
  end
@@ -56,13 +73,18 @@ module GrpcKit
56
73
 
57
74
  private
58
75
 
76
+ def shutdown
77
+ @stop = true
78
+ @io.close
79
+ end
80
+
59
81
  def do_read
60
82
  receive
61
83
  rescue IOError => e
62
- finish
84
+ shutdown
63
85
  raise e
64
86
  rescue DS9::Exception => e
65
- finish
87
+ shutdown
66
88
  if DS9::ERR_EOF == e.code
67
89
  @peer_shutdowned = true
68
90
  return
@@ -93,9 +115,8 @@ module GrpcKit
93
115
  if frame.end_stream?
94
116
  stream.close_remote
95
117
  end
96
-
97
- # when DS9::Frames::Goaway
98
- # when DS9::Frames::RstStream
118
+ when DS9::Frames::Goaway
119
+ handle_goaway(frame)
99
120
  end
100
121
 
101
122
  true
@@ -154,6 +175,25 @@ module GrpcKit
154
175
  # # for nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
155
176
  # def on_invalid_frame_recv(frame, error_code)
156
177
  # end
178
+
179
+ def handle_goaway(frame)
180
+ # shutdown notice
181
+ last_stream_id = frame.last_stream_id
182
+ if last_stream_id == MAX_STREAM_ID && frame.error_code == DS9::NO_ERROR
183
+ @draining = true
184
+ @drain_time = Time.now + 10 # XXX
185
+ @streams.each_value(&:drain)
186
+ end
187
+
188
+ @last_stream_id = last_stream_id
189
+ @streams.each do |id, stream|
190
+ if id > last_stream_id
191
+ stream.close
192
+ end
193
+ end
194
+
195
+ shutdown if @streams.empty?
196
+ end
157
197
  end
158
198
  end
159
199
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ds9'
4
+
5
+ module GrpcKit
6
+ module Session
7
+ class DrainController
8
+ def initialize
9
+ @sent_shutdown_notice = false
10
+ @goaway_sent = false
11
+ @after_one_rtt = false
12
+ # @sent_ping = false
13
+ end
14
+
15
+ def recv_ping_ack
16
+ @after_one_rtt = true
17
+ end
18
+
19
+ def call(session)
20
+ if @goaway_sent
21
+ # session.shutdown
22
+ elsif @sent_shutdown_notice && @after_one_rtt
23
+ session.submit_goaway(DS9::NO_ERROR, session.last_proc_stream_id)
24
+ @goaway_sent = true
25
+ # elsif @sent_shutdown_notice && !@after_one_rtt && !@sent_ping
26
+ # @sent_ping = true
27
+ elsif !@sent_shutdown_notice
28
+ session.submit_shutdown
29
+ @sent_shutdown_notice = true
30
+ session.submit_ping # wait for 1 RTT
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -23,10 +23,13 @@ module GrpcKit
23
23
  'grpc-message',
24
24
  'grpc-status',
25
25
  'grpc-status-details-bin',
26
+ 'grpc-accept-encoding',
26
27
  'te'
27
28
  ].freeze
28
29
 
29
- METADATA_ACCEPTABLE_HEADER = %w[authority user-agent].freeze
30
+ IGNORE_HEADERS = [':method', ':scheme'].freeze
31
+
32
+ METADATA_ACCEPTABLE_HEADER = %w[:authority user-agent].freeze
30
33
  def initialize
31
34
  super({}) # set metadata empty hash
32
35
  end
@@ -38,8 +41,7 @@ module GrpcKit
38
41
  when ':status'
39
42
  self.http_status = Integer(val)
40
43
  when 'content-type'
41
- # TODO
42
- metadata[key] = val
44
+ # self.grpc_encoding = val
43
45
  when 'grpc-encoding'
44
46
  self.grpc_encoding = val
45
47
  when 'grpc-status'
@@ -52,6 +54,10 @@ module GrpcKit
52
54
  # TODO
53
55
  GrpcKit.logger.warn('grpc-status-details-bin is unsupported header now')
54
56
  else
57
+ if IGNORE_HEADERS.include?(key)
58
+ return
59
+ end
60
+
55
61
  if RESERVED_HEADERS.include?(key) && !METADATA_ACCEPTABLE_HEADER.include?(key)
56
62
  return
57
63
  end
@@ -3,11 +3,12 @@
3
3
  require 'ds9'
4
4
  require 'forwardable'
5
5
  require 'grpc_kit/session/stream'
6
- require 'grpc_kit/streams/server_stream'
7
- require 'grpc_kit/transports/server_transport'
6
+ require 'grpc_kit/session/drain_controller'
7
+ require 'grpc_kit/stream/server_stream'
8
+ require 'grpc_kit/transport/server_transport'
8
9
 
9
10
  module GrpcKit
10
- module Sessions
11
+ module Session
11
12
  class ServerSession < DS9::Server
12
13
  extend Forwardable
13
14
 
@@ -24,11 +25,14 @@ module GrpcKit
24
25
  @dispatcher = dispatcher
25
26
  @peer_shutdowned = false
26
27
  @inflights = []
28
+ @drain = nil
27
29
  end
28
30
 
29
31
  def start
30
32
  @io.wait_readable
31
33
  loop do
34
+ break if @stop
35
+
32
36
  invoke
33
37
 
34
38
  if !want_read? && !want_write?
@@ -41,12 +45,23 @@ module GrpcKit
41
45
  end
42
46
  rescue Errno::ECONNRESET, IOError => e
43
47
  GrpcKit.logger.debug(e.message)
44
- finish
48
+ shutdown
49
+ ensure
50
+ GrpcKit.logger.debug('Finish server session')
45
51
  end
46
52
 
47
53
  def run_once
48
54
  return if @stop
49
55
 
56
+ if @drain
57
+ if @streams.empty?
58
+ shutdown
59
+ return
60
+ end
61
+
62
+ @drain.call(self)
63
+ end
64
+
50
65
  if want_read?
51
66
  do_read
52
67
  end
@@ -57,16 +72,14 @@ module GrpcKit
57
72
  end
58
73
 
59
74
  def drain
60
- # could be race condition
61
- @streams.each do |s|
62
- GrpcKit.logger.debug("#{s.stream_id} is draining")
63
- s.drain
64
- end
75
+ @drain ||= GrpcKit::Session::DrainController.new
65
76
  end
66
77
 
67
- def finish
78
+ def shutdown
68
79
  stop
69
80
  @io.close
81
+ rescue StandardError => e
82
+ GrpcKit.logger.debug(e)
70
83
  end
71
84
 
72
85
  private
@@ -77,8 +90,8 @@ module GrpcKit
77
90
 
78
91
  def invoke
79
92
  while (stream = @inflights.pop)
80
- t = GrpcKit::Transports::ServerTransport.new(self, stream)
81
- th = GrpcKit::Streams::ServerStream.new(t)
93
+ t = GrpcKit::Transport::ServerTransport.new(self, stream)
94
+ th = GrpcKit::Stream::ServerStream.new(t)
82
95
  @dispatcher.dispatch(stream.headers.path, th)
83
96
  end
84
97
  end
@@ -86,10 +99,10 @@ module GrpcKit
86
99
  def do_read
87
100
  receive
88
101
  rescue IOError => e
89
- finish
102
+ shutdown
90
103
  raise e
91
104
  rescue DS9::Exception => e
92
- finish
105
+ shutdown
93
106
  if DS9::ERR_EOF == e.code
94
107
  @peer_shutdowned = true
95
108
  return
@@ -136,7 +149,15 @@ module GrpcKit
136
149
  if frame.end_stream?
137
150
  stream.close_remote
138
151
  end
139
-
152
+ when DS9::Frames::Ping
153
+ if frame.ping_ack?
154
+ GrpcKit.logger.debug('ping ack is received')
155
+ # nghttp2 can't send any data once server sent actaul GoAway(not shutdown notice) frame.
156
+ # We want to send data in this case when ClientStreamer or BidiBstreamer which they are sending data in same stream
157
+ # So we have to wait to send actual GoAway frame untill timeout or something
158
+
159
+ # @drain.recv_ping_ack if @drain
160
+ end
140
161
  # when DS9::Frames::Goaway
141
162
  # when DS9::Frames::RstStream
142
163
  end
@@ -192,9 +213,13 @@ module GrpcKit
192
213
  def on_stream_close(stream_id, error_code)
193
214
  GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
194
215
  stream = @streams.delete(stream_id)
195
- return unless stream
216
+ stream.close if stream
196
217
 
197
- stream.close
218
+ if @drain
219
+ if @streams.empty?
220
+ shutdown
221
+ end
222
+ end
198
223
  end
199
224
 
200
225
  # nghttp2_session_callbacks_set_on_data_chunk_recv_callback
@@ -3,9 +3,9 @@
3
3
  require 'grpc_kit/status_codes'
4
4
 
5
5
  module GrpcKit
6
- module Streams
6
+ module Stream
7
7
  class ClientStream
8
- # @params transport [GrpcKit::Transports::ClientTransport]
8
+ # @params transport [GrpcKit::Transport::ClientTransport]
9
9
  # @params config [GrpcKit::MethodConfig]
10
10
  # @params authority [String]
11
11
  def initialize(transport, config, authority:, timeout: nil)
@@ -15,7 +15,7 @@ module GrpcKit
15
15
  @authority = authority
16
16
  @timeout = timeout
17
17
 
18
- @sent_first_msg = false
18
+ @started = false
19
19
  end
20
20
 
21
21
  def send_msg(data, metadata: {}, timeout: nil, last: false)
@@ -31,15 +31,10 @@ module GrpcKit
31
31
  raise GrpcKit::Errors::ResourceExhausted, "Sending message is too large: send=#{req.bytesize}, max=#{limit_size}"
32
32
  end
33
33
 
34
- if @sent_first_msg
35
- # unless metadata.empty?
36
- # raise 'You can attach metadata at first send_msg' # XXX
37
- # end
34
+ if @started
38
35
  @transport.write_data(buf, last: last)
39
36
  else
40
- headers = build_headers(metadata: metadata)
41
- @transport.send_request(buf, headers, last: last)
42
- @sent_first_msg = true
37
+ start_request(buf, metadata: metadata, last: last)
43
38
  end
44
39
  end
45
40
 
@@ -59,7 +54,6 @@ module GrpcKit
59
54
  validate_if_request_start!
60
55
 
61
56
  @transport.close_and_flush
62
- check_status!
63
57
 
64
58
  data = []
65
59
  loop { data.push(do_recv) }
@@ -69,7 +63,7 @@ module GrpcKit
69
63
  private
70
64
 
71
65
  def validate_if_request_start!
72
- unless @sent_first_msg
66
+ unless @started
73
67
  raise 'You should call `send_msg` method to send data'
74
68
  end
75
69
  end
@@ -80,6 +74,8 @@ module GrpcKit
80
74
  if data.nil?
81
75
  check_status!
82
76
  raise StopIteration
77
+ elsif last
78
+ check_status!
83
79
  end
84
80
 
85
81
  compressed, size, buf = *data
@@ -107,39 +103,47 @@ module GrpcKit
107
103
  end
108
104
 
109
105
  def check_status!
110
- headers = @transport.recv_headers
111
-
112
- if headers.grpc_status != GrpcKit::StatusCodes::OK
113
- raise GrpcKit::Errors.from_status_code(headers.grpc_status, headers.status_message)
106
+ if status.code != GrpcKit::StatusCodes::OK
107
+ raise GrpcKit::Errors.from_status_code(status.code, status.msg)
114
108
  else
115
109
  GrpcKit.logger.debug('request is success')
116
110
  end
117
111
  end
118
112
 
119
- def build_headers(metadata: {}, **headers)
120
- # TODO: an order of Headers is important?
113
+ Status = Struct.new(:code, :msg, :metadata)
114
+
115
+ def status
116
+ @status ||=
117
+ begin
118
+ headers = @transport.recv_headers
119
+ Status.new(headers.grpc_status, headers.status_message, headers.metadata)
120
+ end
121
+ end
122
+
123
+ def start_request(buf = nil, last: nil, metadata: {}, timeout: @timeout, authority: @authority)
121
124
  hdrs = {
122
125
  ':method' => 'POST',
123
126
  ':scheme' => 'http',
124
127
  ':path' => @config.path,
125
- ':authority' => @authority,
126
- 'grpc-timeout' => @timeout,
128
+ ':authority' => authority,
129
+ 'grpc-timeout' => timeout,
127
130
  'te' => 'trailers',
128
131
  'content-type' => 'application/grpc',
129
132
  'user-agent' => "grpc-ruby/#{GrpcKit::VERSION} (grpc_kit)",
130
133
  'grpc-accept-encoding' => 'identity,deflate,gzip',
131
- }.merge(headers)
134
+ }
132
135
 
133
136
  metadata.each do |k, v|
134
137
  if k.start_with?('grpc-')
135
138
  # https://github.com/grpc/grpc/blob/ffac9d90b18cb076b1c952faa55ce4e049cbc9a6/doc/PROTOCOL-HTTP2.md
136
- GrpcKit.logger.info("metadata name wich starts with 'grpc-' is reserved for future GRPC")
139
+ GrpcKit.logger.info("metadata name wich starts with 'grpc-' is reserved for future GRPC metadata")
137
140
  else
138
141
  hdrs[k] = v
139
142
  end
140
143
  end
141
144
 
142
- hdrs.compact
145
+ @transport.start_request(buf, hdrs.compact, last: last)
146
+ @started = true
143
147
  end
144
148
  end
145
149
  end
@@ -3,12 +3,12 @@
3
3
  require 'grpc_kit/status_codes'
4
4
 
5
5
  module GrpcKit
6
- module Streams
6
+ module Stream
7
7
  class ServerStream
8
8
  # @params transport [GrpcKit::transports::ServerTransport]
9
9
  def initialize(transport)
10
10
  @transport = transport
11
- @sent_first_msg = false
11
+ @started = false
12
12
  end
13
13
 
14
14
  def invoke(rpc)
@@ -21,11 +21,7 @@ module GrpcKit
21
21
  send_status(status: GrpcKit::StatusCodes::UNKNOWN, msg: e.message, metadata: {})
22
22
  end
23
23
 
24
- def send_msg(data, protobuf, last: false, limit_size: nil)
25
- if last
26
- send_trailer # TODO: pass trailer metadata
27
- end
28
-
24
+ def send_msg(data, protobuf, last: false, limit_size: nil, initial_metadata: {}, trailing_metadata: {})
29
25
  buf =
30
26
  begin
31
27
  protobuf.encode(data)
@@ -37,11 +33,13 @@ module GrpcKit
37
33
  raise GrpcKit::Errors::ResourceExhausted, "Sending message is too large: send=#{req.bytesize}, max=#{limit_size}"
38
34
  end
39
35
 
40
- @transport.write_data(buf, last: last)
41
- return if @sent_first_msg
42
-
43
- send_response({})
44
- @sent_first_msg = true
36
+ if last
37
+ send_status(data: buf, metadata: trailing_metadata)
38
+ elsif @started
39
+ @transport.write_data(buf)
40
+ else
41
+ start_response(buf, metadata: initial_metadata)
42
+ end
45
43
  end
46
44
 
47
45
  def recv_msg(protobuf, last: false, limit_size: nil)
@@ -68,7 +66,7 @@ module GrpcKit
68
66
  begin
69
67
  protobuf.decode(buf)
70
68
  rescue ArgumentError => e
71
- raise GrpcKit::Errors::Internal, "Error while decoding in Server: #{e}"
69
+ raise GrpcKit::Errors::Internal, "Error while decoding in server: #{e}"
72
70
  end
73
71
  end
74
72
 
@@ -76,29 +74,31 @@ module GrpcKit
76
74
  loop { yield(recv) }
77
75
  end
78
76
 
79
- def send_status(status: GrpcKit::StatusCodes::INTERNAL, msg: nil, metadata: {})
80
- send_trailer(status: status, msg: msg, metadata: metadata)
81
- return if @sent_first_msg
77
+ def send_status(data: nil, status: GrpcKit::StatusCodes::OK, msg: nil, metadata: {})
78
+ @transport.write_data(data, last: true) if data
79
+ write_trailers(status, msg, metadata)
82
80
 
83
- send_response({})
84
- @sent_first_msg = true
81
+ start_response unless @started
85
82
  end
86
83
 
87
- def send_trailer(status: GrpcKit::StatusCodes::OK, msg: nil, metadata: {})
88
- trailer = metadata.dup
89
- trailer['grpc-status'] = status.to_s
90
- if msg
91
- trailer['grpc-message'] = msg
92
- end
84
+ private
85
+
86
+ def start_response(data = nil, metadata: {})
87
+ h = { ':status' => '200', 'content-type' => 'application/grpc' }
88
+ h['accept-encoding'] = 'identity'
93
89
 
94
- @transport.write_trailers_data(trailer)
90
+ @transport.write_data(data) if data
91
+ @transport.start_response(h.merge(metadata))
92
+ @started = true
95
93
  end
96
94
 
97
- def send_response(headers)
98
- h = { ':status' => '200', 'content-type' => 'application/grpc' }.merge(headers)
99
- h['accept-encoding'] = 'identity'
95
+ def write_trailers(status, msg, metadata)
96
+ trailers = { 'grpc-status' => status.to_s }
97
+ if msg
98
+ trailers['grpc-message'] = msg
99
+ end
100
100
 
101
- @transport.send_response(h)
101
+ @transport.write_trailers(trailers.merge(metadata))
102
102
  end
103
103
  end
104
104
  end
@@ -4,18 +4,18 @@ require 'grpc_kit/transport/packable'
4
4
  require 'grpc_kit/transport/send_buffer'
5
5
 
6
6
  module GrpcKit
7
- module Transports
7
+ module Transport
8
8
  class ClientTransport
9
9
  include GrpcKit::Transport::Packable
10
10
 
11
- # @params session [GrpcKit::Session::Server|GrpcKit::Session::Client]
11
+ # @params session [GrpcKit::Session::ClientSession]
12
12
  def initialize(session)
13
13
  @session = session
14
14
  @stream = nil # set later
15
15
  @deferred = false
16
16
  end
17
17
 
18
- def send_request(data, header, last: false)
18
+ def start_request(data, header, last: false)
19
19
  @stream = @session.send_request(GrpcKit::Transport::SendBuffer.new, header)
20
20
  write_data(data, last: last)
21
21
  end
@@ -3,11 +3,11 @@
3
3
  require 'grpc_kit/transport/packable'
4
4
 
5
5
  module GrpcKit
6
- module Transports
6
+ module Transport
7
7
  class ServerTransport
8
8
  include GrpcKit::Transport::Packable
9
9
 
10
- # @params session [GrpcKit::Sessions::ServerSession]
10
+ # @params session [GrpcKit::Session::ServerSession]
11
11
  # @params stream [GrpcKit::Session::Stream]
12
12
  def initialize(session, stream)
13
13
  @session = session
@@ -23,7 +23,7 @@ module GrpcKit
23
23
  end
24
24
  end
25
25
 
26
- def send_response(headers)
26
+ def start_response(headers)
27
27
  @session.submit_response(@stream.stream_id, headers)
28
28
  end
29
29
 
@@ -35,7 +35,7 @@ module GrpcKit
35
35
  unpack(read(last: last))
36
36
  end
37
37
 
38
- def write_trailers_data(trailer)
38
+ def write_trailers(trailer)
39
39
  @stream.write_trailers_data(trailer)
40
40
  @stream.end_write
41
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrpcKit
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grpc_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - ganmacs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-30 00:00:00.000000000 Z
11
+ date: 2018-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ds9
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.0
19
+ version: 1.2.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.0
26
+ version: 1.2.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: google-protobuf
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -208,19 +208,20 @@ files:
208
208
  - lib/grpc_kit/rpcs/server_server_streamer.rb
209
209
  - lib/grpc_kit/server.rb
210
210
  - lib/grpc_kit/session/buffer.rb
211
+ - lib/grpc_kit/session/client_session.rb
212
+ - lib/grpc_kit/session/drain_controller.rb
211
213
  - lib/grpc_kit/session/headers.rb
212
214
  - lib/grpc_kit/session/io.rb
215
+ - lib/grpc_kit/session/server_session.rb
213
216
  - lib/grpc_kit/session/stream.rb
214
217
  - lib/grpc_kit/session/stream_status.rb
215
- - lib/grpc_kit/sessions/client_session.rb
216
- - lib/grpc_kit/sessions/server_session.rb
217
218
  - lib/grpc_kit/status_codes.rb
218
- - lib/grpc_kit/streams/client_stream.rb
219
- - lib/grpc_kit/streams/server_stream.rb
219
+ - lib/grpc_kit/stream/client_stream.rb
220
+ - lib/grpc_kit/stream/server_stream.rb
221
+ - lib/grpc_kit/transport/client_transport.rb
220
222
  - lib/grpc_kit/transport/packable.rb
221
223
  - lib/grpc_kit/transport/send_buffer.rb
222
- - lib/grpc_kit/transports/client_transport.rb
223
- - lib/grpc_kit/transports/server_transport.rb
224
+ - lib/grpc_kit/transport/server_transport.rb
224
225
  - lib/grpc_kit/version.rb
225
226
  homepage: https://github.com/ganmacs/grpc_kit
226
227
  licenses: