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.
- checksums.yaml +4 -4
- data/.rubocop.yml +22 -6
- data/examples/interceptors/client_logging_interceptor.rb +48 -0
- data/examples/interceptors/server_logging_interceptor.rb +44 -0
- data/examples/routeguide_client.rb +63 -3
- data/examples/routeguide_server.rb +86 -3
- data/grpc_kit.gemspec +1 -1
- data/lib/grpc.rb +1 -0
- data/lib/grpc_kit/client.rb +31 -79
- data/lib/grpc_kit/errors.rb +18 -8
- data/lib/grpc_kit/grpc/dsl.rb +19 -21
- data/lib/grpc_kit/grpc/generic_service.rb +1 -1
- data/lib/grpc_kit/grpc/interceptor.rb +57 -0
- data/lib/grpc_kit/grpc/logger.rb +17 -0
- data/lib/grpc_kit/interceptors.rb +11 -0
- data/lib/grpc_kit/interceptors/client_streamer.rb +31 -0
- data/lib/grpc_kit/interceptors/request_response.rb +70 -0
- data/lib/grpc_kit/interceptors/server_streamer.rb +33 -0
- data/lib/grpc_kit/interceptors/streaming.rb +70 -0
- data/lib/grpc_kit/method_config.rb +34 -0
- data/lib/grpc_kit/protobuffer.rb +20 -0
- data/lib/grpc_kit/rpc_desc.rb +103 -27
- data/lib/grpc_kit/rpcs.rb +11 -0
- data/lib/grpc_kit/rpcs/base.rb +30 -0
- data/lib/grpc_kit/rpcs/bidi_streamer.rb +18 -0
- data/lib/grpc_kit/rpcs/call.rb +27 -0
- data/lib/grpc_kit/rpcs/client_streamer.rb +38 -0
- data/lib/grpc_kit/rpcs/request_response.rb +50 -0
- data/lib/grpc_kit/rpcs/server_streamer.rb +42 -0
- data/lib/grpc_kit/server.rb +19 -38
- data/lib/grpc_kit/session/buffer.rb +58 -0
- data/lib/grpc_kit/session/client.rb +100 -52
- data/lib/grpc_kit/session/duration.rb +96 -0
- data/lib/grpc_kit/session/headers.rb +65 -0
- data/lib/grpc_kit/session/io.rb +56 -0
- data/lib/grpc_kit/session/server.rb +107 -68
- data/lib/grpc_kit/session/stream.rb +32 -40
- data/lib/grpc_kit/stream.rb +71 -0
- data/lib/grpc_kit/streams/client.rb +87 -0
- data/lib/grpc_kit/streams/packable.rb +60 -0
- data/lib/grpc_kit/streams/send_buffer.rb +48 -0
- data/lib/grpc_kit/streams/server.rb +36 -0
- data/lib/grpc_kit/streams/stream.rb +58 -0
- data/lib/grpc_kit/version.rb +1 -1
- metadata +32 -5
- 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
|
-
|
10
|
-
|
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
|
19
|
-
|
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
|
-
|
23
|
-
|
24
|
-
break
|
25
|
-
elsif !stream.exist_data?
|
26
|
-
receive
|
32
|
+
def start(stream_id)
|
33
|
+
stream = @streams.fetch(stream_id)
|
27
34
|
|
28
|
-
|
29
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
#
|
52
|
-
def
|
53
|
-
|
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
|
-
#
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
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
|