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