grpc_kit 0.1.3 → 0.1.4
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/.travis.yml +0 -1
- data/TODO.md +1 -1
- data/examples/helloworld_client.rb +5 -1
- data/examples/routeguide_client.rb +5 -2
- data/lib/grpc_kit/calls/client_client_streamer.rb +29 -0
- data/lib/grpc_kit/calls/client_request_response.rb +25 -0
- data/lib/grpc_kit/calls/client_server_streamer.rb +29 -0
- data/lib/grpc_kit/calls/server_client_streamer.rb +32 -0
- data/lib/grpc_kit/calls/server_request_response.rb +32 -0
- data/lib/grpc_kit/calls/server_server_streamer.rb +32 -0
- data/lib/grpc_kit/calls.rb +39 -0
- data/lib/grpc_kit/client.rb +17 -17
- data/lib/grpc_kit/grpc_time.rb +100 -0
- data/lib/grpc_kit/interceptors/client_client_streamer.rb +19 -0
- data/lib/grpc_kit/interceptors/client_request_response.rb +39 -0
- data/lib/grpc_kit/interceptors/client_server_streamer.rb +19 -0
- data/lib/grpc_kit/interceptors/server_client_streamer.rb +15 -0
- data/lib/grpc_kit/interceptors/server_request_response.rb +36 -0
- data/lib/grpc_kit/interceptors/server_server_streamer.rb +17 -0
- data/lib/grpc_kit/interceptors.rb +64 -5
- data/lib/grpc_kit/rpc_desc.rb +18 -2
- data/lib/grpc_kit/rpcs/client_bidi_streamer.rb +11 -0
- data/lib/grpc_kit/rpcs/client_client_streamer.rb +23 -0
- data/lib/grpc_kit/rpcs/client_request_response.rb +31 -0
- data/lib/grpc_kit/rpcs/client_server_streamer.rb +19 -0
- data/lib/grpc_kit/rpcs/server_bidi_streamer.rb +10 -0
- data/lib/grpc_kit/rpcs/server_client_streamer.rb +24 -0
- data/lib/grpc_kit/rpcs/server_request_response.rb +26 -0
- data/lib/grpc_kit/rpcs/server_server_streamer.rb +26 -0
- data/lib/grpc_kit/rpcs.rb +21 -5
- data/lib/grpc_kit/server.rb +16 -7
- data/lib/grpc_kit/session/headers.rb +2 -2
- data/lib/grpc_kit/session/stream.rb +5 -0
- data/lib/grpc_kit/{session/client.rb → sessions/client_session.rb} +5 -6
- data/lib/grpc_kit/{session/server.rb → sessions/server_session.rb} +21 -8
- data/lib/grpc_kit/streams/client_stream.rb +146 -0
- data/lib/grpc_kit/streams/server_stream.rb +105 -0
- data/lib/grpc_kit/{streams → transport}/packable.rb +2 -2
- data/lib/grpc_kit/{streams → transport}/send_buffer.rb +1 -1
- data/lib/grpc_kit/transports/client_transport.rb +96 -0
- data/lib/grpc_kit/transports/server_transport.rb +68 -0
- data/lib/grpc_kit/version.rb +1 -1
- metadata +32 -21
- data/lib/grpc_kit/interceptors/client_streamer.rb +0 -31
- data/lib/grpc_kit/interceptors/request_response.rb +0 -70
- data/lib/grpc_kit/interceptors/server_streamer.rb +0 -33
- data/lib/grpc_kit/interceptors/streaming.rb +0 -70
- data/lib/grpc_kit/rpcs/base.rb +0 -30
- data/lib/grpc_kit/rpcs/bidi_streamer.rb +0 -18
- data/lib/grpc_kit/rpcs/call.rb +0 -27
- data/lib/grpc_kit/rpcs/client_streamer.rb +0 -38
- data/lib/grpc_kit/rpcs/error.rb +0 -23
- data/lib/grpc_kit/rpcs/request_response.rb +0 -64
- data/lib/grpc_kit/rpcs/server_streamer.rb +0 -42
- data/lib/grpc_kit/session/duration.rb +0 -97
- data/lib/grpc_kit/stream.rb +0 -120
- data/lib/grpc_kit/streams/client.rb +0 -113
- data/lib/grpc_kit/streams/server.rb +0 -54
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'grpc_kit/rpcs/base'
|
4
|
-
require 'grpc_kit/status_codes'
|
5
|
-
|
6
|
-
module GrpcKit
|
7
|
-
module Rpcs
|
8
|
-
module Client
|
9
|
-
class RequestResponse < Base
|
10
|
-
def invoke(session, request, authority:, metadata: {}, timeout: nil, **opts)
|
11
|
-
cs = GrpcKit::Streams::Client.new(config: @config, session: session, authority: authority)
|
12
|
-
|
13
|
-
call = GrpcKit::Rpcs::Call.new(metadata, @config.method_name, @config.service_name, cs)
|
14
|
-
@config.interceptor.intercept(request, call, metadata) do |r, c, m|
|
15
|
-
if timeout
|
16
|
-
# XXX: when timeout.to_timeout is 0
|
17
|
-
Timeout.timeout(timeout.to_timeout, GrpcKit::Errors::DeadlineExceeded) do
|
18
|
-
c.send_msg(r, timeout: timeout.to_s, metadata: m, last: true)
|
19
|
-
c.recv(last: true)
|
20
|
-
end
|
21
|
-
else
|
22
|
-
c.send_msg(r, metadata: m, last: true)
|
23
|
-
c.recv(last: true)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module Server
|
31
|
-
class RequestResponse < Base
|
32
|
-
def invoke(stream, session)
|
33
|
-
ss = GrpcKit::Streams::Server.new(stream: stream, session: session, config: @config)
|
34
|
-
call = GrpcKit::Rpcs::Call.new(stream.headers.metadata, @config.method_name, @config.service_name, ss)
|
35
|
-
|
36
|
-
begin
|
37
|
-
do_invoke(ss, call)
|
38
|
-
rescue GrpcKit::Errors::BadStatus => e
|
39
|
-
ss.send_status(status: e.code, msg: e.reason, metadata: {}) # TODO: metadata should be set
|
40
|
-
rescue StandardError => e
|
41
|
-
ss.send_status(status: GrpcKit::StatusCodes::UNKNOWN, msg: e.message, metadata: {})
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def do_invoke(ss, call)
|
48
|
-
request = ss.recv(last: true)
|
49
|
-
|
50
|
-
resp =
|
51
|
-
if @config.interceptor
|
52
|
-
@config.interceptor.intercept(request, call) do |req, c|
|
53
|
-
@handler.send(@config.ruby_style_method_name, req, c)
|
54
|
-
end
|
55
|
-
else
|
56
|
-
@handler.send(@config.ruby_style_method_name, request, call)
|
57
|
-
end
|
58
|
-
|
59
|
-
ss.send_msg(resp, last: true)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'grpc_kit/rpcs/base'
|
4
|
-
|
5
|
-
module GrpcKit
|
6
|
-
module Rpcs
|
7
|
-
module Client
|
8
|
-
class ServerStreamer < Base
|
9
|
-
def invoke(session, request, authority:, metadata: {}, timeout: nil, **opts)
|
10
|
-
cs = GrpcKit::Streams::Client.new(config: @config, session: session, authority: authority)
|
11
|
-
call = GrpcKit::Rpcs::Call.new(metadata, @config.method_name, @config.service_name, cs)
|
12
|
-
|
13
|
-
@config.interceptor.intercept(call, metadata) do |c, m|
|
14
|
-
c.send_msg(request, metadata: m, last: true)
|
15
|
-
c
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module Server
|
22
|
-
class ServerStreamer < Base
|
23
|
-
def invoke(stream, session)
|
24
|
-
ss = GrpcKit::Streams::Server.new(stream: stream, session: session, config: @config)
|
25
|
-
call = GrpcKit::Rpcs::Call.new(stream.headers.metadata, @config.method_name, @config.service_name, ss)
|
26
|
-
|
27
|
-
if @config.interceptor
|
28
|
-
@config.interceptor.intercept(call) do |c|
|
29
|
-
request = c.recv(last: true)
|
30
|
-
@handler.send(@config.ruby_style_method_name, request, c)
|
31
|
-
end
|
32
|
-
else
|
33
|
-
request = call.recv(last: true)
|
34
|
-
@handler.send(@config.ruby_style_method_name, request, call)
|
35
|
-
end
|
36
|
-
|
37
|
-
ss.send_trailer
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,97 +0,0 @@
|
|
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
|
-
value = value.dup
|
16
|
-
size = value.size
|
17
|
-
if size < 2
|
18
|
-
raise "Invalid format: too short #{value}"
|
19
|
-
end
|
20
|
-
|
21
|
-
unit = value.slice!(-1, 1)
|
22
|
-
d = Duration.new(0, 0, 0, 0)
|
23
|
-
n = Integer(value)
|
24
|
-
|
25
|
-
case unit
|
26
|
-
when 'H'
|
27
|
-
d.sec = n * HOUR
|
28
|
-
when 'M'
|
29
|
-
d.sec = n * MIN
|
30
|
-
when 'S'
|
31
|
-
d.sec = n
|
32
|
-
when 'm'
|
33
|
-
d.msec = n
|
34
|
-
when 'u'
|
35
|
-
d.usec = n
|
36
|
-
when 'n'
|
37
|
-
d.nsec = n
|
38
|
-
else
|
39
|
-
raise "Invalid unit `#{unit}`: #{value + unit} "
|
40
|
-
end
|
41
|
-
d
|
42
|
-
end
|
43
|
-
|
44
|
-
def to_timeout
|
45
|
-
v = 0
|
46
|
-
|
47
|
-
if nsec && (nsec != 0)
|
48
|
-
v += (NANO_SEC * nsec)
|
49
|
-
end
|
50
|
-
|
51
|
-
if usec && (usec != 0)
|
52
|
-
v += (MICRO_SEC * usec)
|
53
|
-
end
|
54
|
-
|
55
|
-
if msec && (msec != 0)
|
56
|
-
v += (MILL_SEC * msec)
|
57
|
-
end
|
58
|
-
|
59
|
-
if sec
|
60
|
-
v += sec
|
61
|
-
end
|
62
|
-
|
63
|
-
v
|
64
|
-
end
|
65
|
-
|
66
|
-
# @params val [Numeric]
|
67
|
-
def self.from_numeric(val)
|
68
|
-
case val
|
69
|
-
when nil
|
70
|
-
when Numeric
|
71
|
-
if val < 0
|
72
|
-
Duration.new(MAX_TIMEOUT, 0, 0, 0)
|
73
|
-
elsif val == 0
|
74
|
-
Duration.new(0, 0, 0, 0)
|
75
|
-
else
|
76
|
-
Duration.new(val, 0, 0, 0)
|
77
|
-
end
|
78
|
-
else
|
79
|
-
raise "Cannot make timeout from #{val}"
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# TODO
|
84
|
-
def to_s
|
85
|
-
if nsec && (nsec != 0)
|
86
|
-
"#{nsec}n"
|
87
|
-
elsif usec && (usec != 0)
|
88
|
-
"#{usec}u"
|
89
|
-
elsif msec && (msec != 0)
|
90
|
-
"#{msec}m"
|
91
|
-
elsif sec
|
92
|
-
"#{sec}S"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
data/lib/grpc_kit/stream.rb
DELETED
@@ -1,120 +0,0 @@
|
|
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? close_remote? headers] => :@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 do
|
25
|
-
data = recv
|
26
|
-
return if data.nil?
|
27
|
-
|
28
|
-
yield(data)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def send(data, last: false, limit_size: nil)
|
33
|
-
b =
|
34
|
-
begin
|
35
|
-
@protobuf.encode(data)
|
36
|
-
rescue ArgumentError => e
|
37
|
-
raise GrpcKit::Errors::Internal, "Error while encoding: #{e}"
|
38
|
-
end
|
39
|
-
|
40
|
-
req = pack(b)
|
41
|
-
if limit_size && req.bytesize > limit_size
|
42
|
-
raise GrpcKit::Errors::ResourceExhausted, "Sending message is too large: send=#{req.bytesize}, max=#{limit_size}"
|
43
|
-
end
|
44
|
-
|
45
|
-
@stream.write_send_data(req, last: last)
|
46
|
-
end
|
47
|
-
|
48
|
-
def recv(last: false, limit_size: nil)
|
49
|
-
data = unpack(read(last: last))
|
50
|
-
|
51
|
-
return nil unless data
|
52
|
-
|
53
|
-
compressed, size, buf = *data
|
54
|
-
|
55
|
-
unless size == buf.size
|
56
|
-
raise "inconsistent data: #{buf}"
|
57
|
-
end
|
58
|
-
|
59
|
-
if limit_size && size > limit_size
|
60
|
-
raise GrpcKit::Errors::ResourceExhausted, "Receving message is too large: recevied=#{size}, max=#{limit_size}"
|
61
|
-
end
|
62
|
-
|
63
|
-
if compressed
|
64
|
-
raise 'compress option is unsupported'
|
65
|
-
end
|
66
|
-
|
67
|
-
begin
|
68
|
-
@protobuf.decode(buf)
|
69
|
-
rescue ArgumentError => e
|
70
|
-
raise GrpcKit::Errors::Internal, "Error while decoding #{e}"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def send_trailer(status: GrpcKit::StatusCodes::OK, msg: nil, metadata: {})
|
75
|
-
trailer = metadata.dup
|
76
|
-
trailer['grpc-status'] = status.to_s
|
77
|
-
if msg
|
78
|
-
trailer['grpc-message'] = msg
|
79
|
-
end
|
80
|
-
|
81
|
-
@stream.write_trailers_data(trailer)
|
82
|
-
@stream.end_write
|
83
|
-
end
|
84
|
-
|
85
|
-
# TODO: use actual data
|
86
|
-
def submit_response(_header = nil, piggyback_trailer: false)
|
87
|
-
headers = { ':status' => '200', 'content-type' => 'application/grpc' }
|
88
|
-
|
89
|
-
# ds9 does not support nthttp2_submit_{response|request} without body
|
90
|
-
# if piggyback_trailer
|
91
|
-
# headers.merge!(@stream.trailer_data)
|
92
|
-
# @stream.need_trailer = false
|
93
|
-
# else
|
94
|
-
headers['accept-encoding'] = 'identity'
|
95
|
-
# end
|
96
|
-
|
97
|
-
@session.submit_response(@stream.stream_id, headers)
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def read(last: false)
|
103
|
-
loop do
|
104
|
-
data = @stream.read_recv_data(last: last)
|
105
|
-
return data unless data.empty?
|
106
|
-
|
107
|
-
if @stream.close_remote?
|
108
|
-
# it do not receive data which we need, it may receive invalid grpc-status
|
109
|
-
unless @stream.end_read?
|
110
|
-
return nil
|
111
|
-
end
|
112
|
-
|
113
|
-
return nil
|
114
|
-
end
|
115
|
-
|
116
|
-
@session.run_once
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'grpc_kit/stream'
|
4
|
-
require 'grpc_kit/streams/send_buffer'
|
5
|
-
require 'grpc_kit/status_codes'
|
6
|
-
|
7
|
-
module GrpcKit
|
8
|
-
module Streams
|
9
|
-
class Client
|
10
|
-
def initialize(session:, config:, authority:)
|
11
|
-
@config = config
|
12
|
-
@session = session
|
13
|
-
@stream = nil
|
14
|
-
@authority = authority
|
15
|
-
end
|
16
|
-
|
17
|
-
def send_msg(data, metadata: {}, timeout: nil, last: false)
|
18
|
-
if @stream
|
19
|
-
# unless metadata.empty?
|
20
|
-
# raise 'You can attach metadata at first send_msg' # XXX
|
21
|
-
# end
|
22
|
-
|
23
|
-
unless @stream.end_write?
|
24
|
-
@session.resume_data(@stream.stream_id)
|
25
|
-
end
|
26
|
-
else
|
27
|
-
headers = build_headers(metadata: metadata, timeout: timeout)
|
28
|
-
stream = @session.start_request(GrpcKit::Streams::SendBuffer.new, headers)
|
29
|
-
@stream = GrpcKit::Stream.new(protobuf: @config.protobuf, session: @session, stream: stream)
|
30
|
-
end
|
31
|
-
|
32
|
-
@stream.send(data, last: last, limit_size: @config.max_send_message_size)
|
33
|
-
@session.run_once
|
34
|
-
end
|
35
|
-
|
36
|
-
def each(&block)
|
37
|
-
unless @stream
|
38
|
-
raise 'You should call `send` method to send data'
|
39
|
-
end
|
40
|
-
|
41
|
-
@stream.each(&block)
|
42
|
-
end
|
43
|
-
|
44
|
-
def recv(last: false)
|
45
|
-
unless @stream
|
46
|
-
raise 'You should call `send` method to send data'
|
47
|
-
end
|
48
|
-
|
49
|
-
data = @stream.recv(last: last, limit_size: @config.max_receive_message_size)
|
50
|
-
|
51
|
-
if data.nil?
|
52
|
-
check_status!
|
53
|
-
raise StopIteration
|
54
|
-
end
|
55
|
-
|
56
|
-
data
|
57
|
-
end
|
58
|
-
|
59
|
-
def close_and_recv
|
60
|
-
unless @stream
|
61
|
-
raise 'You should call `send` method to send data'
|
62
|
-
end
|
63
|
-
|
64
|
-
unless @stream.end_write?
|
65
|
-
@session.resume_data(@stream.stream_id)
|
66
|
-
end
|
67
|
-
|
68
|
-
@stream.end_write
|
69
|
-
@session.start(@stream.stream_id)
|
70
|
-
@stream.end_read
|
71
|
-
|
72
|
-
check_status!
|
73
|
-
|
74
|
-
data = []
|
75
|
-
@stream.each { |d| data.push(d) }
|
76
|
-
data
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
def check_status!
|
82
|
-
# XXX: wait until half close (remote) to get grpc-status
|
83
|
-
until @stream.close_remote?
|
84
|
-
@session.run_once
|
85
|
-
end
|
86
|
-
|
87
|
-
if @stream.headers.grpc_status != GrpcKit::StatusCodes::OK
|
88
|
-
raise GrpcKit::Errors.from_status_code(@stream.headers.grpc_status, @stream.headers.status_message)
|
89
|
-
else
|
90
|
-
GrpcKit.logger.debug('request is success')
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def build_headers(metadata: {}, timeout: nil, **headers)
|
95
|
-
hdrs = metadata.merge(headers).merge(
|
96
|
-
':method' => 'POST',
|
97
|
-
':scheme' => 'http',
|
98
|
-
':path' => @config.path,
|
99
|
-
':authority' => @authority,
|
100
|
-
'te' => 'trailers',
|
101
|
-
'content-type' => 'application/grpc',
|
102
|
-
'user-agent' => "grpc-ruby/#{GrpcKit::VERSION} (grpc_kit)",
|
103
|
-
'grpc-accept-encoding' => 'identity,deflate,gzip',
|
104
|
-
)
|
105
|
-
if timeout
|
106
|
-
hdrs['grpc-timeout'] = timeout
|
107
|
-
end
|
108
|
-
|
109
|
-
hdrs
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
require 'grpc_kit/stream'
|
6
|
-
require 'grpc_kit/status_codes'
|
7
|
-
|
8
|
-
module GrpcKit
|
9
|
-
module Streams
|
10
|
-
class Server
|
11
|
-
extend Forwardable
|
12
|
-
|
13
|
-
delegate %i[each recv] => :@stream
|
14
|
-
|
15
|
-
def initialize(stream:, session:, config:)
|
16
|
-
@stream = GrpcKit::Stream.new(protobuf: config.protobuf, session: session, stream: stream)
|
17
|
-
@config = config
|
18
|
-
@sent_first_msg = false
|
19
|
-
end
|
20
|
-
|
21
|
-
def send_msg(data, last: false)
|
22
|
-
if last
|
23
|
-
@stream.send_trailer # TODO: pass trailer metadata
|
24
|
-
end
|
25
|
-
|
26
|
-
@stream.send(data, last: last, limit_size: @config.max_send_message_size)
|
27
|
-
return if @sent_first_msg
|
28
|
-
|
29
|
-
@stream.submit_response
|
30
|
-
@sent_first_msg = true
|
31
|
-
end
|
32
|
-
|
33
|
-
def recv(last: false)
|
34
|
-
data = @stream.recv(last: last, limit_size: @config.max_receive_message_size)
|
35
|
-
raise StopIteration if data.nil?
|
36
|
-
|
37
|
-
data
|
38
|
-
end
|
39
|
-
|
40
|
-
def send_trailer
|
41
|
-
@stream.send_trailer # TODO: pass trailer metadata
|
42
|
-
@stream.end_write
|
43
|
-
end
|
44
|
-
|
45
|
-
def send_status(status: GrpcKit::StatusCodes::INTERNAL, msg: nil, metadata: {})
|
46
|
-
@stream.send_trailer(status: status, msg: msg, metadata: metadata)
|
47
|
-
return if @sent_first_msg
|
48
|
-
|
49
|
-
@stream.submit_response(piggyback_trailer: true)
|
50
|
-
@sent_first_msg = true
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|