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