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
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrpcKit
4
+ module Session
5
+ class Buffer
6
+ attr_accessor :finish
7
+
8
+ def initialize(buffer: nil)
9
+ @buffer = buffer
10
+ @end_read = false
11
+ @end_write = false
12
+ @finish = false
13
+ @write_byte_size = 0
14
+ end
15
+
16
+ def write(data, last: false)
17
+ end_write if last
18
+ @write_byte_size += data.size
19
+
20
+ if @buffer
21
+ @buffer << data
22
+ else
23
+ @buffer = data
24
+ end
25
+ end
26
+
27
+ def read(len = nil, last: false)
28
+ end_read if last
29
+
30
+ if @buffer.nil?
31
+ return ''
32
+ end
33
+
34
+ if len
35
+ @buffer.slice!(0...len)
36
+ else
37
+ @buffer.slice!(0..-1)
38
+ end
39
+ end
40
+
41
+ def end_read?
42
+ @end_read
43
+ end
44
+
45
+ def end_write?
46
+ @end_write
47
+ end
48
+
49
+ def end_read
50
+ @end_read = true
51
+ end
52
+
53
+ def end_write
54
+ @end_write = true
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,75 +1,140 @@
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 Client < DS9::Client
9
- # @io [GrpcKit::IO::XXX]
10
- def initialize(io, handler)
10
+ extend Forwardable
11
+
12
+ delegate %i[send_event recv_event] => :@io
13
+
14
+ # @params io [GrpcKit::Session::IO]
15
+ def initialize(io, handler, opts = {})
11
16
  super() # initialize DS9::Session
12
17
 
13
18
  @io = io
14
19
  @streams = {}
15
20
  @handler = handler
21
+ @opts = opts
16
22
  end
17
23
 
18
- def start(stream_id)
19
- stream = GrpcKit::Session::Stream.new(stream_id: stream_id)
24
+ def start_request(data, headers)
25
+ stream_id = submit_request(headers, data)
26
+ stream = GrpcKit::Session::Stream.new(stream_id: stream_id, send_data: data)
27
+ stream.stream_id = stream_id
20
28
  @streams[stream_id] = stream
29
+ stream
30
+ end
21
31
 
22
- while want_read? || want_write?
23
- if stream.closed?
24
- break
25
- elsif !stream.exist_data?
26
- receive
32
+ def start(stream_id)
33
+ stream = @streams.fetch(stream_id)
27
34
 
28
- send
29
- else
35
+ loop do
36
+ if (!want_read? && !want_write?) || stream.end_stream?
30
37
  break
31
- # GrpcKit.logger.info("unknown #{stream}")
32
38
  end
39
+
40
+ run_once
41
+ end
42
+ end
43
+
44
+ def run_once
45
+ return if @stop
46
+
47
+ if want_read?
48
+ do_read
49
+ end
50
+
51
+ if want_write?
52
+ send
33
53
  end
34
- # invalid if receive and send are not called
35
54
  end
36
55
 
37
56
  private
38
57
 
39
- # for nghttp2_session_callbacks_set_send_callback
40
- # override
41
- def send_event(string)
42
- @io.write(string)
58
+ def do_read
59
+ receive
60
+ rescue IOError => e
61
+ finish
62
+ raise e
63
+ rescue DS9::Exception => e
64
+ finish
65
+ if DS9::ERR_EOF == e.code
66
+ @peer_shutdowned = true
67
+ return
68
+ # raise EOFError
69
+ end
70
+
71
+ raise e
43
72
  end
44
73
 
45
- # for nghttp2_session_callbacks_set_recv_callback
46
- # override
47
- def recv_event(length)
48
- @io.read(length)
74
+ # nghttp2_session_callbacks_set_on_frame_send_callback
75
+ def on_frame_recv(frame)
76
+ GrpcKit.logger.debug("on_frame_recv #{frame}")
77
+ case frame
78
+ when DS9::Frames::Data
79
+ stream = @streams[frame.stream_id]
80
+
81
+ if frame.end_stream?
82
+ stream.remote_end_stream = true
83
+ end
84
+
85
+ unless stream.inflight
86
+ stream.inflight = true
87
+ end
88
+
89
+ when DS9::Frames::Headers
90
+ stream = @streams[frame.stream_id]
91
+
92
+ if frame.end_stream?
93
+ stream.remote_end_stream = true
94
+ end
95
+
96
+ # when DS9::Frames::Goaway
97
+ # when DS9::Frames::RstStream
98
+ end
99
+
100
+ true
49
101
  end
50
102
 
51
- # for nghttp2_session_callbacks_set_on_data_chunk_recv_callback
52
- def on_data_chunk_recv(stream_id, data, flags)
53
- @handler.on_data_chunk_recv(@streams[stream_id], data)
103
+ # nghttp2_session_callbacks_set_on_frame_send_callback
104
+ def on_frame_send(frame)
105
+ GrpcKit.logger.debug("on_frame_send #{frame}")
106
+ case frame
107
+ when DS9::Frames::Data, DS9::Frames::Headers
108
+ stream = @streams[frame.stream_id]
109
+ if frame.end_stream?
110
+ stream.local_end_stream = true
111
+ end
112
+ end
113
+
114
+ true
54
115
  end
55
116
 
56
- # provider for nghttp2_submit_response
57
- # def on_data_source_read(stream_id, length)
58
- # end
117
+ # nghttp2_session_callbacks_set_on_stream_close_callback
118
+ def on_stream_close(stream_id, error_code)
119
+ GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
120
+ stream = @streams.delete(stream_id)
121
+ return unless stream
59
122
 
60
- # for nghttp2_session_callbacks_set_on_frame_send_callback
61
- # def on_frame_recv(frame)
62
- # GrpcKit.logger.debug("on_frame_recv #{frame}")
63
- # end
123
+ stream.end_stream
124
+ end
125
+
126
+ # nghttp2_session_callbacks_set_on_data_chunk_recv_callback
127
+ def on_data_chunk_recv(stream_id, data, _flags)
128
+ stream = @streams[stream_id]
129
+ if stream
130
+ stream.pending_recv_data.write(data)
131
+ end
132
+ end
64
133
 
65
134
  # # for nghttp2_session_callbacks_set_on_frame_not_send_callback
66
135
  # def on_frame_not_send(frame, reason)
67
136
  # end
68
137
 
69
- # # for nghttp2_session_callbacks_set_on_frame_send_callback
70
- # def on_frame_send(frame, reason)
71
- # end
72
-
73
138
  # # for nghttp2_session_callbacks_set_on_header_callback
74
139
  # def on_header(name, value, frame, flags)
75
140
  # end
@@ -85,23 +150,6 @@ module GrpcKit
85
150
  # # for nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
86
151
  # def on_invalid_frame_recv(frame, error_code)
87
152
  # end
88
-
89
- # for nghttp2_session_callbacks_set_on_stream_close_callback
90
- def on_stream_close(stream_id, error_code)
91
- GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
92
- stream = @streams.delete(stream_id)
93
- if stream
94
- stream.close
95
- end
96
- end
97
-
98
- # # for nghttp2_session_callbacks_set_on_data_chunk_recv_callback
99
- # def on_data_chunk_recv(id, data, flags)
100
- # end
101
-
102
- # # nghttp2_session_callbacks_set_before_frame_send_callback
103
- # def before_frame_send(frame)
104
- # end
105
153
  end
106
154
  end
107
155
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrpcKit
4
+ module Session
5
+ class Duration < Struct.new(:sec, :msec, :usec, :nsec)
6
+ MAX_TIMEOUT = 10**9 - 1
7
+ HOUR = 60 * 60
8
+ MIN = 60
9
+ MILL_SEC = 10**-3
10
+ MICRO_SEC = 10**-6
11
+ NANO_SEC = 10**-9
12
+
13
+ # @params val [String]
14
+ def self.decode(value)
15
+ size = value.size
16
+ if size < 2
17
+ raise "Invalid format: too short #{value}"
18
+ end
19
+
20
+ unit = value[(size - 1)..size]
21
+ d = Duration.new(0, 0, 0, 0)
22
+ n = Integer(value[0..size])
23
+
24
+ case u
25
+ when 'H'
26
+ d.sec = n * HOUR
27
+ when 'M'
28
+ d.sec = n * MIN
29
+ when 'S'
30
+ d.sec = n
31
+ when 'm'
32
+ d.msec = n
33
+ when 'u'
34
+ d.usec = n
35
+ when 'n'
36
+ d.nsec = n
37
+ else
38
+ raise "Invalid unit `#{u}`: #{unit} "
39
+ end
40
+ d
41
+ end
42
+
43
+ def to_timeout
44
+ v = 0
45
+
46
+ if nsec && (nsec != 0)
47
+ v += (NANO_SEC * nsec)
48
+ end
49
+
50
+ if usec && (usec != 0)
51
+ v += (MICRO_SEC * usec)
52
+ end
53
+
54
+ if msec && (msec != 0)
55
+ v += (MILL_SEC * msec)
56
+ end
57
+
58
+ if sec
59
+ v += sec
60
+ end
61
+
62
+ v
63
+ end
64
+
65
+ # @params val [Numeric]
66
+ def self.from_numeric(val)
67
+ case val
68
+ when nil
69
+ when Numeric
70
+ if val < 0
71
+ Duration.new(MAX_TIMEOUT, 0, 0, 0)
72
+ elsif val == 0
73
+ Duration.new(0, 0, 0, 0)
74
+ else
75
+ Duration.new(val, 0, 0, 0)
76
+ end
77
+ else
78
+ raise "Cannot make timeout from #{val}"
79
+ end
80
+ end
81
+
82
+ # TODO
83
+ def to_s
84
+ if nsec && (nsec != 0)
85
+ "#{nsec}n"
86
+ elsif usec && (usec != 0)
87
+ "#{usec}u"
88
+ elsif msec && (msec != 0)
89
+ "#{msec}m"
90
+ elsif sec
91
+ "#{sec}S"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/session/duration'
4
+
5
+ module GrpcKit
6
+ module Session
7
+ Headers = Struct.new(
8
+ :metadata,
9
+ :path,
10
+ :grpc_encoding,
11
+ :grpc_status,
12
+ :status_message,
13
+ :timeout,
14
+ :method,
15
+ :http_status,
16
+ ) do
17
+
18
+ RESERVED_HEADERS = [
19
+ 'content-type',
20
+ 'user-agent',
21
+ 'grpc-message-type',
22
+ 'grpc-encoding',
23
+ 'grpc-message',
24
+ 'grpc-status',
25
+ 'grpc-status-details-bin',
26
+ 'te'
27
+ ].freeze
28
+
29
+ METADATA_ACCEPTABLE_HEADER = %w[authority user-agent].freeze
30
+ def initialize
31
+ super({}) # set metadata empty hash
32
+ end
33
+
34
+ def add(key, val)
35
+ case key
36
+ when ':path'
37
+ self.path = val
38
+ when ':status'
39
+ self.http_status = val.to_i
40
+ when 'content-type'
41
+ # TODO
42
+ metadata[key] = val
43
+ when 'grpc-encoding'
44
+ self.grpc_encoding = val
45
+ when 'grpc-status'
46
+ self.grpc_status = val.to_i
47
+ when 'grpc-timeout'
48
+ self.timeout = Duration.decod(v)
49
+ when 'grpc-message'
50
+ # TODO
51
+ GrpcKit.logger.warn('grpc-message is unsupported header now')
52
+ when 'grpc-status-details-bin'
53
+ # TODO
54
+ GrpcKit.logger.warn('grpc-status-details-bin is unsupported header now')
55
+ else
56
+ if RESERVED_HEADERS.include?(key) && !METADATA_ACCEPTABLE_HEADER.include?(key)
57
+ return
58
+ end
59
+
60
+ metadata[key] = val
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ds9'
4
+
5
+ module GrpcKit
6
+ module Session
7
+ class IO
8
+ def initialize(io)
9
+ @io = io
10
+ end
11
+
12
+ def close
13
+ @io.close
14
+ end
15
+
16
+ def recv_event(length)
17
+ data = @io.read_nonblock(length, nil, exception: false)
18
+
19
+ case data
20
+ when :wait_readable
21
+ DS9::ERR_WOULDBLOCK
22
+ when nil # nil means EOF
23
+ DS9::ERR_EOF
24
+ else
25
+ data
26
+ end
27
+ end
28
+
29
+ def send_event(data)
30
+ remain = data.bytesize
31
+ size = remain
32
+ while remain > 0
33
+ begin
34
+ remain -= @io.syswrite(data)
35
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
36
+ unless IO.select(nil, [io], nil, 1)
37
+ raise 'timeout writing data'
38
+ end
39
+ end
40
+
41
+ data = data.byteslice(remain..-1)
42
+ end
43
+
44
+ size
45
+ end
46
+
47
+ def wait_readable
48
+ @io.wait_writable
49
+ end
50
+
51
+ def flush
52
+ @io.flush
53
+ end
54
+ end
55
+ end
56
+ end