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
@@ -1,31 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
3
4
  require 'ds9'
4
5
  require 'grpc_kit/session/stream'
5
6
 
6
7
  module GrpcKit
7
8
  module Session
8
9
  class Server < DS9::Server
9
- # @io [GrpcKit::IO::XXX]
10
+ extend Forwardable
11
+
12
+ delegate %i[send_event recv_event] => :@io
13
+
14
+ # @params io [GrpcKit::Session::IO]
10
15
  def initialize(io, handler)
11
- super() # initialize DS9::Server
16
+ super() # initialize DS9::Session
12
17
 
13
18
  @io = io
14
19
  @streams = {}
15
20
  @stop = false
16
21
  @handler = handler
22
+ @peer_shutdowned = false
23
+ @inflights = []
17
24
  end
18
25
 
19
26
  def start
20
27
  @io.wait_readable
21
- until @stop && (want_read? || want_write?)
22
- if want_write?
23
- send
24
- end
28
+ loop do
29
+ invoke_handler
25
30
 
26
- if want_read?
27
- receive
31
+ if !want_read? && !want_write?
32
+ break
33
+ elsif @peer_shutdowned && !want_write?
34
+ break
28
35
  end
36
+
37
+ run_once
38
+ end
39
+ end
40
+
41
+ def run_once
42
+ return if @stop
43
+
44
+ if want_read?
45
+ do_read
46
+ end
47
+
48
+ if want_write?
49
+ send
29
50
  end
30
51
  end
31
52
 
@@ -33,79 +54,103 @@ module GrpcKit
33
54
  @stop = true
34
55
  end
35
56
 
57
+ def finish
58
+ stop
59
+ @io.close
60
+ end
61
+
36
62
  private
37
63
 
38
- # provider for nghttp2_submit_response
39
- # override
64
+ def invoke_handler
65
+ while (stream = @inflights.pop)
66
+ @handler.dispatch(stream, self)
67
+ end
68
+ end
69
+
70
+ def do_read
71
+ receive
72
+ rescue IOError => e
73
+ finish
74
+ raise e
75
+ rescue DS9::Exception => e
76
+ finish
77
+ if DS9::ERR_EOF == e.code
78
+ @peer_shutdowned = true
79
+ return
80
+ # raise EOFError
81
+ end
82
+
83
+ raise e
84
+ end
85
+
86
+ # `provider` for nghttp2_submit_response
40
87
  def on_data_source_read(stream_id, length)
41
88
  GrpcKit.logger.debug("on_data_source_read #{stream_id}, lenght=#{length}")
42
89
 
43
- data = @streams[stream_id].read(length)
44
- if data.nil?
90
+ stream = @streams[stream_id]
91
+ data = @streams[stream_id].pending_send_data.read(length)
92
+ if data.empty? && stream.end_write?
93
+ # TODO
45
94
  submit_trailer(stream_id, 'grpc-status' => '0')
46
- false # nil mean END_STREAM
95
+
96
+ false # means EOF and END_STREAM
47
97
  else
48
98
  data
49
99
  end
50
100
  end
51
101
 
52
- # for nghttp2_session_callbacks_set_send_callback
53
- # override
54
- def send_event(string)
55
- GrpcKit.logger.debug('send_event')
56
-
57
- @io.write(string)
58
- end
59
-
60
- # for nghttp2_session_callbacks_set_recv_callback
61
- # override
62
- def recv_event(length)
63
- # GrpcKit.logger.debug("recv_event #{length}")
64
- @io.read(length)
65
- end
66
-
67
- # for nghttp2_session_callbacks_set_on_frame_send_callback
102
+ # nghttp2_session_callbacks_set_on_frame_recv_callback
68
103
  def on_frame_recv(frame)
69
104
  GrpcKit.logger.debug("on_frame_recv #{frame}")
105
+
70
106
  case frame
71
107
  when DS9::Frames::Data
72
- # need to port NGHTTP2_FLAG_END_STREAM to check frame.flag has it
108
+
73
109
  stream = @streams[frame.stream_id]
74
- resp = @handler.on_frame_data_recv(stream)
75
- unless resp
76
- return # TODO
110
+
111
+ if frame.end_stream?
112
+ stream.remote_end_stream = true
77
113
  end
78
114
 
79
- submit_response(
80
- frame.stream_id,
81
- ':status' => '200',
82
- 'content-type' => 'application/grpc',
83
- 'accept-encoding' => 'identity,gzip',
84
- )
85
- # when DS9::Frames::Headers
86
- # need to port NGHTTP2_FLAG_END_STREAM to check frame.flag has it
87
- # when DS9::Frames::Goaway
88
- # when DS9::Frames::RstStream
89
- else
90
- GrpcKit.logger.info("unsupport frame #{frame}")
115
+ unless stream.inflight
116
+ stream.inflight = true
117
+ @inflights << stream
118
+ end
119
+ when DS9::Frames::Headers
120
+ stream = @streams[frame.stream_id]
121
+
122
+ if frame.end_stream?
123
+ stream.remote_end_stream = true
124
+ end
125
+
126
+ # when DS9::Frames::Goaway
127
+ # when DS9::Frames::RstStream
91
128
  end
92
129
 
93
130
  true
94
131
  end
95
132
 
96
- # for nghttp2_session_callbacks_set_on_frame_send_callback
133
+ # nghttp2_session_callbacks_set_on_frame_send_callback
97
134
  def on_frame_send(frame)
98
135
  GrpcKit.logger.debug("on_frame_send #{frame}")
136
+ case frame
137
+ when DS9::Frames::Data, DS9::Frames::Headers
138
+ stream = @streams[frame.stream_id]
139
+ if frame.end_stream?
140
+ stream.local_end_stream = true
141
+ end
142
+ end
143
+
99
144
  true
100
145
  end
101
146
 
102
- # for nghttp2_session_callbacks_set_on_frame_not_send_callback
147
+ # nghttp2_session_callbacks_set_on_frame_not_send_callback
103
148
  def on_frame_not_send(frame, reason)
104
149
  GrpcKit.logger.debug("on_frame_not_send frame=#{frame}, reason=#{reason}")
105
150
  true
106
151
  end
107
152
 
108
- # for nghttp2_session_callbacks_set_on_begin_headers_callback
153
+ # nghttp2_session_callbacks_set_on_begin_headers_callback
109
154
  def on_begin_headers(header)
110
155
  stream_id = header.stream_id
111
156
  GrpcKit.logger.debug("on_begin_header stream_id=#{stream_id}")
@@ -117,38 +162,32 @@ module GrpcKit
117
162
  @streams[stream_id] = GrpcKit::Session::Stream.new(stream_id: stream_id)
118
163
  end
119
164
 
120
- # for nghttp2_session_callbacks_set_on_header_callback
165
+ # nghttp2_session_callbacks_set_on_header_callback
121
166
  def on_header(name, value, frame, _flags)
122
- @streams[frame.stream_id].headers[name] = value
123
- end
124
-
125
- # for nghttp2_session_callbacks_set_on_begin_frame_callback
126
- def on_begin_frame(frame_header)
127
- GrpcKit.logger.debug("on_begin_frame #{frame_header}")
128
- true
167
+ stream = @streams[frame.stream_id]
168
+ stream.add_header(name, value)
129
169
  end
130
170
 
131
- # for nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
132
171
  def on_invalid_frame_recv(frame, error_code)
133
- GrpcKit.logger.debug("on_invalid_frame_recv #{error_code}")
172
+ GrpcKit.logger.debug("on_invalid_frame_recv #{frame} error_code=#{error_code}")
134
173
  true
135
174
  end
136
175
 
137
- # for nghttp2_session_callbacks_set_on_stream_close_callback
176
+ # nghttp2_session_callbacks_set_on_stream_close_callback
138
177
  def on_stream_close(stream_id, error_code)
139
178
  GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
140
- @streams.delete(stream_id)
141
- end
179
+ stream = @streams.delete(stream_id)
180
+ return unless stream
142
181
 
143
- # for nghttp2_session_callbacks_set_on_data_chunk_recv_callback
144
- def on_data_chunk_recv(stream_id, data, _flags)
145
- @handler.on_data_chunk_recv(@streams[stream_id], data)
182
+ stream.end_stream
146
183
  end
147
184
 
148
- # nghttp2_session_callbacks_set_before_frame_send_callback
149
- def before_frame_send(frame)
150
- GrpcKit.logger.debug("before_frame_send frame=#{frame}")
151
- true
185
+ # nghttp2_session_callbacks_set_on_data_chunk_recv_callback
186
+ def on_data_chunk_recv(stream_id, data, _flags)
187
+ stream = @streams[stream_id]
188
+ if stream
189
+ stream.pending_recv_data.write(data)
190
+ end
152
191
  end
153
192
  end
154
193
  end
@@ -1,67 +1,59 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- require 'ds9'
3
+ require 'forwardable'
4
+ require 'grpc_kit/session/buffer'
5
+ require 'grpc_kit/session/headers'
4
6
 
5
7
  module GrpcKit
6
8
  module Session
7
9
  class Stream
8
- attr_reader :headers
9
- attr_accessor :data
10
+ extend Forwardable
10
11
 
11
- def initialize(stream_id:, end_read_stream: false, end_write_stream: false)
12
- @stream_id = stream_id
13
- @end_read_stream = end_read_stream
14
- @end_write_stream = end_write_stream
15
- @end_stream = false
16
- @headers = {}
17
-
18
- @data = ''
19
- @write_data = StringIO.new
20
- end
12
+ delegate end_write: :@pending_send_data
13
+ delegate end_read: :@pending_recv_data
21
14
 
22
- def close
23
- @end_stream = true
24
- end
15
+ attr_reader :headers, :pending_send_data, :pending_recv_data
16
+ attr_accessor :local_end_stream, :remote_end_stream, :inflight, :stream_id
25
17
 
26
- def closed?
27
- @end_stream
28
- end
18
+ def initialize(stream_id:, send_data: GrpcKit::Session::Buffer.new)
19
+ @stream_id = stream_id
20
+ @end_read_stream = false
21
+ @headers = GrpcKit::Session::Headers.new
22
+ @pending_send_data = send_data
23
+ @pending_recv_data = GrpcKit::Session::Buffer.new
29
24
 
30
- def eq?(stream_id)
31
- @stream_id == stream_id
25
+ @local_end_stream = false
26
+ @remote_end_stream = false
27
+ @inflight = false
32
28
  end
33
29
 
34
- def exist_data?
35
- !@data.empty?
30
+ def write_send_data(data, last: false)
31
+ @pending_send_data.write(data, last: last)
36
32
  end
37
33
 
38
- def recv(data)
39
- @data = data # XXX
34
+ def read_recv_data(last: false)
35
+ @pending_recv_data.read(last: last)
40
36
  end
41
37
 
42
- # TODO
43
- def recv2(data)
44
- @data << data
38
+ def end_write?
39
+ @local_end_stream || @pending_send_data.end_write?
45
40
  end
46
41
 
47
- # TODO: name
48
- def send(data)
49
- @write_data = data
50
- # @write_data.write(data)
42
+ def end_read?
43
+ @remote_end_stream || @pending_recv_data.end_read?
51
44
  end
52
45
 
53
- # TODO: name
54
- def read(len)
55
- # @write_data.rewind
56
- @write_data.read(len)
46
+ def end_stream?
47
+ end_read? && end_write?
57
48
  end
58
49
 
59
- def read_stream_end?
60
- @end_read_stream
50
+ def end_stream
51
+ end_read
52
+ end_write
61
53
  end
62
54
 
63
- def write_stream_end?
64
- @end_write_stream
55
+ def add_header(name, value)
56
+ @headers.add(name, value)
65
57
  end
66
58
  end
67
59
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'grpc_kit/streams/packable'
5
+
6
+ module GrpcKit
7
+ class Stream
8
+ include GrpcKit::Streams::Packable
9
+
10
+ extend Forwardable
11
+
12
+ delegate %i[stream_id end_write end_read end_write? end_read?] => :@stream
13
+
14
+ # @params protobuf [GrpcKit::Protobuffer]
15
+ # @params session [GrpcKit::Session::Server|GrpcKit::Session::Client]
16
+ # @params stream [GrpcKit::Session::Stream] primitive H2 stream id
17
+ def initialize(protobuf:, session:, stream:)
18
+ @protobuf = protobuf
19
+ @session = session
20
+ @stream = stream
21
+ end
22
+
23
+ def each
24
+ loop { yield(recv) }
25
+ end
26
+
27
+ def send(data, last: false)
28
+ req = @protobuf.encode(data)
29
+ @stream.write_send_data(pack(req), last: last)
30
+ end
31
+
32
+ def recv(last: false)
33
+ data = unpack(read(last: last))
34
+
35
+ unless data
36
+ raise StopIteration
37
+ end
38
+
39
+ compressed, size, buf = *data
40
+
41
+ unless size == buf.size
42
+ raise "inconsistent data: #{buf}"
43
+ end
44
+
45
+ if compressed
46
+ raise 'compress option is unsupported'
47
+ end
48
+
49
+ @protobuf.decode(buf)
50
+ end
51
+
52
+ private
53
+
54
+ def read(last: false)
55
+ loop do
56
+ data = @stream.read_recv_data(last: last)
57
+ if data.empty?
58
+ # Consider `end_read` is set in this invocation
59
+ if @stream.end_read? && !last
60
+ return nil
61
+ end
62
+
63
+ @session.run_once
64
+ redo
65
+ end
66
+
67
+ return data
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/stream'
4
+ require 'grpc_kit/streams/send_buffer'
5
+
6
+ module GrpcKit
7
+ module Streams
8
+ class Client
9
+ def initialize(path:, protobuf:, session:, authority:)
10
+ @path = path
11
+ @session = session
12
+ @protobuf = protobuf
13
+ @stream = nil
14
+ @authority = authority
15
+ end
16
+
17
+ def send_msg(data, metadata: {}, timeout: nil, last: false)
18
+ if @stream
19
+ unless @stream.end_write?
20
+ @session.resume_data(@stream.stream_id)
21
+ end
22
+ else
23
+ headers = build_headers(metadata: metadata, timeout: timeout)
24
+ stream = @session.start_request(GrpcKit::Streams::SendBuffer.new, headers)
25
+ @stream = GrpcKit::Stream.new(protobuf: @protobuf, session: @session, stream: stream)
26
+ end
27
+
28
+ @stream.send(data, last: last)
29
+ @session.run_once
30
+ end
31
+
32
+ def each(&block)
33
+ unless @stream
34
+ raise 'You should call `send` method to send data'
35
+ end
36
+
37
+ @stream.each(&block)
38
+ end
39
+
40
+ def recv(last: false)
41
+ unless @stream
42
+ raise 'You should call `send` method to send data'
43
+ end
44
+
45
+ @stream.recv(last: last)
46
+ end
47
+
48
+ def close_and_recv
49
+ unless @stream
50
+ raise 'You should call `send` method to send data'
51
+ end
52
+
53
+ unless @stream.end_write?
54
+ @session.resume_data(@stream.stream_id)
55
+ end
56
+
57
+ @stream.end_write
58
+ @session.start(@stream.stream_id)
59
+ @stream.end_read
60
+
61
+ data = []
62
+ @stream.each { |d| data.push(d) }
63
+ data
64
+ end
65
+
66
+ private
67
+
68
+ def build_headers(metadata: {}, timeout: nil, **headers)
69
+ hdrs = metadata.merge(headers).merge(
70
+ ':method' => 'POST',
71
+ ':scheme' => 'http',
72
+ ':path' => @path,
73
+ ':authority' => @authority,
74
+ 'te' => 'trailers',
75
+ 'content-type' => 'application/grpc',
76
+ 'user-agent' => "grpc-ruby/#{GrpcKit::VERSION} (grpc_kit)",
77
+ 'grpc-accept-encoding' => 'identity,deflate,gzip',
78
+ )
79
+ if timeout
80
+ hdrs['grpc-timeout'] = timeout
81
+ end
82
+
83
+ hdrs
84
+ end
85
+ end
86
+ end
87
+ end