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
@@ -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