grpc_kit 0.1.2 → 0.1.3

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.
@@ -27,23 +27,14 @@ module GrpcKit
27
27
  end
28
28
 
29
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
- rescue IOError => e
40
- raise IOError, e # TODO
41
- end
42
-
43
- data = data.byteslice(remain..-1)
44
- end
30
+ return 0 if data.empty?
45
31
 
46
- size
32
+ bytes = @io.write_nonblock(data, exception: false)
33
+ if bytes == :wait_writable
34
+ DS9::ERR_WOULDBLOCK
35
+ else
36
+ bytes
37
+ end
47
38
  end
48
39
 
49
40
  def wait_readable
@@ -36,7 +36,8 @@ module GrpcKit
36
36
 
37
37
  run_once
38
38
  end
39
- rescue Errno::ECONNRESET, IOError
39
+ rescue Errno::ECONNRESET, IOError => e
40
+ GrpcKit.logger.debug(e.message)
40
41
  finish
41
42
  end
42
43
 
@@ -92,10 +93,9 @@ module GrpcKit
92
93
  stream = @streams[stream_id]
93
94
  data = @streams[stream_id].pending_send_data.read(length)
94
95
  if data.empty? && stream.end_write?
95
- # TODO
96
- submit_trailer(stream_id, 'grpc-status' => '0')
97
-
98
- false # means EOF and END_STREAM
96
+ submit_trailer(stream_id, stream.trailer_data)
97
+ # trailer header
98
+ false
99
99
  else
100
100
  data
101
101
  end
@@ -107,11 +107,10 @@ module GrpcKit
107
107
 
108
108
  case frame
109
109
  when DS9::Frames::Data
110
-
111
110
  stream = @streams[frame.stream_id]
112
111
 
113
112
  if frame.end_stream?
114
- stream.remote_end_stream = true
113
+ stream.close_remote
115
114
  end
116
115
 
117
116
  unless stream.inflight
@@ -122,7 +121,7 @@ module GrpcKit
122
121
  stream = @streams[frame.stream_id]
123
122
 
124
123
  if frame.end_stream?
125
- stream.remote_end_stream = true
124
+ stream.close_remote
126
125
  end
127
126
 
128
127
  # when DS9::Frames::Goaway
@@ -139,7 +138,7 @@ module GrpcKit
139
138
  when DS9::Frames::Data, DS9::Frames::Headers
140
139
  stream = @streams[frame.stream_id]
141
140
  if frame.end_stream?
142
- stream.local_end_stream = true
141
+ stream.close_local
143
142
  end
144
143
  end
145
144
 
@@ -166,6 +165,7 @@ module GrpcKit
166
165
 
167
166
  # nghttp2_session_callbacks_set_on_header_callback
168
167
  def on_header(name, value, frame, _flags)
168
+ GrpcKit.logger.debug("#{name} => #{value}")
169
169
  stream = @streams[frame.stream_id]
170
170
  stream.add_header(name, value)
171
171
  end
@@ -181,7 +181,7 @@ module GrpcKit
181
181
  stream = @streams.delete(stream_id)
182
182
  return unless stream
183
183
 
184
- stream.end_stream
184
+ stream.close
185
185
  end
186
186
 
187
187
  # nghttp2_session_callbacks_set_on_data_chunk_recv_callback
@@ -3,28 +3,34 @@
3
3
  require 'forwardable'
4
4
  require 'grpc_kit/session/buffer'
5
5
  require 'grpc_kit/session/headers'
6
+ require 'grpc_kit/session/stream_status'
6
7
 
7
8
  module GrpcKit
8
9
  module Session
9
10
  class Stream
10
11
  extend Forwardable
11
12
 
12
- delegate end_write: :@pending_send_data
13
- delegate end_read: :@pending_recv_data
13
+ delegate %i[end_write end_write?] => :@pending_send_data
14
+ delegate %i[end_read end_read?] => :@pending_recv_data
15
+ delegate %i[close close_remote close_local close? close_remote? close_local?] => :@status
14
16
 
15
- attr_reader :headers, :pending_send_data, :pending_recv_data
16
- attr_accessor :local_end_stream, :remote_end_stream, :inflight, :stream_id
17
+ attr_reader :headers, :pending_send_data, :pending_recv_data, :trailer_data, :status
18
+ attr_accessor :inflight, :stream_id
17
19
 
18
- def initialize(stream_id:, send_data: GrpcKit::Session::Buffer.new)
20
+ def initialize(stream_id:, send_data: nil, recv_data: nil)
19
21
  @stream_id = stream_id
20
22
  @end_read_stream = false
21
23
  @headers = GrpcKit::Session::Headers.new
22
- @pending_send_data = send_data
23
- @pending_recv_data = GrpcKit::Session::Buffer.new
24
+ @pending_send_data = send_data || GrpcKit::Session::Buffer.new
25
+ @pending_recv_data = recv_data || GrpcKit::Session::Buffer.new
24
26
 
25
- @local_end_stream = false
26
- @remote_end_stream = false
27
27
  @inflight = false
28
+ @trailer_data = {}
29
+ @status = GrpcKit::Session::StreamStatus.new
30
+ end
31
+
32
+ def write_trailers_data(tariler)
33
+ @trailer_data = tariler
28
34
  end
29
35
 
30
36
  def write_send_data(data, last: false)
@@ -35,23 +41,6 @@ module GrpcKit
35
41
  @pending_recv_data.read(last: last)
36
42
  end
37
43
 
38
- def end_write?
39
- @local_end_stream || @pending_send_data.end_write?
40
- end
41
-
42
- def end_read?
43
- @remote_end_stream || @pending_recv_data.end_read?
44
- end
45
-
46
- def end_stream?
47
- end_read? && end_write?
48
- end
49
-
50
- def end_stream
51
- end_read
52
- end_write
53
- end
54
-
55
44
  def add_header(name, value)
56
45
  @headers.add(name, value)
57
46
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: false
2
+
3
+ module GrpcKit
4
+ module Session
5
+ class StreamStatus
6
+ OPEN = 0
7
+ CLOSE = 1
8
+ HALF_CLOSE_REMOTE = 2
9
+ HALF_CLOSE_LOCAL = 3
10
+
11
+ def initialize
12
+ @status = OPEN
13
+ end
14
+
15
+ def close_local
16
+ if @status == OPEN
17
+ @status = HALF_CLOSE_LOCAL
18
+ elsif @status == HALF_CLOSE_REMOTE
19
+ @status = CLOSE
20
+ elsif @status == HALF_CLOSE_LOCAL
21
+ # nothing
22
+ else
23
+ raise 'stream is already closed'
24
+ end
25
+ end
26
+
27
+ def close_remote
28
+ if @status == OPEN
29
+ @status = HALF_CLOSE_REMOTE
30
+ elsif @status == HALF_CLOSE_LOCAL
31
+ @status = CLOSE
32
+ elsif @status == HALF_CLOSE_REMOTE
33
+ # nothing
34
+ else
35
+ raise 'stream is already closed'
36
+ end
37
+ end
38
+
39
+ def close
40
+ @status = CLOSE
41
+ end
42
+
43
+ def close_local?
44
+ (@status == HALF_CLOSE_LOCAL) || close?
45
+ end
46
+
47
+ def close_remote?
48
+ (@status == HALF_CLOSE_REMOTE) || close?
49
+ end
50
+
51
+ def close?
52
+ @status == CLOSE
53
+ end
54
+ end
55
+ end
56
+ end
@@ -2,23 +2,44 @@
2
2
 
3
3
  module GrpcKit
4
4
  module StatusCodes
5
- OK = 0
6
- CANCELLED = 1
7
- UNKNOWN = 2
8
- INVALID_ARGUMENT = 3
9
- DEADLINE_EXCEEDED = 4
10
- NOT_FOUND = 5
11
- ALREADY_EXISTS = 6
12
- PERMISSION_DENIED = 7
13
- RESOURCE_EXHAUSTED = 8
14
- FAILED_PRECONDITION = 9
15
- ABORTED = 10
16
- OUT_OF_RANGE = 11
17
- UNIMPLEMENTED = 12
18
- INTERNAL = 13
19
- UNAVAILABLE = 14
20
- DATA_LOSS = 15
21
- UNAUTHENTICATED = 16
22
- DO_NOT_USE = -1
5
+ OK = '0'
6
+ CANCELLED = '1'
7
+ UNKNOWN = '2'
8
+ INVALID_ARGUMENT = '3'
9
+ DEADLINE_EXCEEDED = '4'
10
+ NOT_FOUND = '5'
11
+ ALREADY_EXISTS = '6'
12
+ PERMISSION_DENIED = '7'
13
+ RESOURCE_EXHAUSTED = '8'
14
+ FAILED_PRECONDITION = '9'
15
+ ABORTED = '10'
16
+ OUT_OF_RANGE = '11'
17
+ UNIMPLEMENTED = '12'
18
+ INTERNAL = '13'
19
+ UNAVAILABLE = '14'
20
+ DATA_LOSS = '15'
21
+ UNAUTHENTICATED = '16'
22
+ DO_NOT_USE = '-1'
23
+
24
+ CODE_NAME = {
25
+ OK => 'OK',
26
+ CANCELLED => 'CANCELLED',
27
+ UNKNOWN => 'UNKNOWN',
28
+ INVALID_ARGUMENT => 'INVALID_ARGUMENT',
29
+ DEADLINE_EXCEEDED => 'DEADLINE_EXCEEDED',
30
+ NOT_FOUND => 'NOT_FOUND',
31
+ ALREADY_EXISTS => 'ALREADY_EXISTS',
32
+ PERMISSION_DENIED => 'PERMISSION_DENIED',
33
+ RESOURCE_EXHAUSTED => 'RESOURCE_EXHAUSTED',
34
+ FAILED_PRECONDITION => 'FAILED_PRECONDITION',
35
+ ABORTED => 'ABORTED',
36
+ OUT_OF_RANGE => 'OUT_OF_RANGE',
37
+ UNIMPLEMENTED => 'UNIMPLEMENTED',
38
+ INTERNAL => 'INTERNAL',
39
+ UNAVAILABLE => 'UNAVAILABLE',
40
+ DATA_LOSS => 'DATA_LOSS',
41
+ UNAUTHENTICATED => 'UNAUTHENTICATED',
42
+ DO_NOT_USE => 'DO_NOT_USE',
43
+ }.freeze
23
44
  end
24
45
  end
@@ -9,7 +9,7 @@ module GrpcKit
9
9
 
10
10
  extend Forwardable
11
11
 
12
- delegate %i[stream_id end_write end_read end_write? end_read?] => :@stream
12
+ delegate %i[stream_id end_write end_read end_write? end_read? close_remote? headers] => :@stream
13
13
 
14
14
  # @params protobuf [GrpcKit::Protobuffer]
15
15
  # @params session [GrpcKit::Session::Server|GrpcKit::Session::Client]
@@ -21,20 +21,34 @@ module GrpcKit
21
21
  end
22
22
 
23
23
  def each
24
- loop { yield(recv) }
24
+ loop do
25
+ data = recv
26
+ return if data.nil?
27
+
28
+ yield(data)
29
+ end
25
30
  end
26
31
 
27
- def send(data, last: false)
28
- req = @protobuf.encode(data)
29
- @stream.write_send_data(pack(req), last: last)
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)
30
46
  end
31
47
 
32
- def recv(last: false)
48
+ def recv(last: false, limit_size: nil)
33
49
  data = unpack(read(last: last))
34
50
 
35
- unless data
36
- raise StopIteration
37
- end
51
+ return nil unless data
38
52
 
39
53
  compressed, size, buf = *data
40
54
 
@@ -42,11 +56,45 @@ module GrpcKit
42
56
  raise "inconsistent data: #{buf}"
43
57
  end
44
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
+
45
63
  if compressed
46
64
  raise 'compress option is unsupported'
47
65
  end
48
66
 
49
- @protobuf.decode(buf)
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)
50
98
  end
51
99
 
52
100
  private
@@ -54,17 +102,18 @@ module GrpcKit
54
102
  def read(last: false)
55
103
  loop do
56
104
  data = @stream.read_recv_data(last: last)
57
- if data.empty?
58
- # Consider `end_read` is set in this invocation
59
- if @stream.end_read? && !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?
60
110
  return nil
61
111
  end
62
112
 
63
- @session.run_once
64
- redo
113
+ return nil
65
114
  end
66
115
 
67
- return data
116
+ @session.run_once
68
117
  end
69
118
  end
70
119
  end
@@ -2,30 +2,34 @@
2
2
 
3
3
  require 'grpc_kit/stream'
4
4
  require 'grpc_kit/streams/send_buffer'
5
+ require 'grpc_kit/status_codes'
5
6
 
6
7
  module GrpcKit
7
8
  module Streams
8
9
  class Client
9
- def initialize(path:, protobuf:, session:, authority:)
10
- @path = path
10
+ def initialize(session:, config:, authority:)
11
+ @config = config
11
12
  @session = session
12
- @protobuf = protobuf
13
13
  @stream = nil
14
14
  @authority = authority
15
15
  end
16
16
 
17
17
  def send_msg(data, metadata: {}, timeout: nil, last: false)
18
18
  if @stream
19
+ # unless metadata.empty?
20
+ # raise 'You can attach metadata at first send_msg' # XXX
21
+ # end
22
+
19
23
  unless @stream.end_write?
20
24
  @session.resume_data(@stream.stream_id)
21
25
  end
22
26
  else
23
27
  headers = build_headers(metadata: metadata, timeout: timeout)
24
28
  stream = @session.start_request(GrpcKit::Streams::SendBuffer.new, headers)
25
- @stream = GrpcKit::Stream.new(protobuf: @protobuf, session: @session, stream: stream)
29
+ @stream = GrpcKit::Stream.new(protobuf: @config.protobuf, session: @session, stream: stream)
26
30
  end
27
31
 
28
- @stream.send(data, last: last)
32
+ @stream.send(data, last: last, limit_size: @config.max_send_message_size)
29
33
  @session.run_once
30
34
  end
31
35
 
@@ -42,7 +46,14 @@ module GrpcKit
42
46
  raise 'You should call `send` method to send data'
43
47
  end
44
48
 
45
- @stream.recv(last: last)
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
46
57
  end
47
58
 
48
59
  def close_and_recv
@@ -58,6 +69,8 @@ module GrpcKit
58
69
  @session.start(@stream.stream_id)
59
70
  @stream.end_read
60
71
 
72
+ check_status!
73
+
61
74
  data = []
62
75
  @stream.each { |d| data.push(d) }
63
76
  data
@@ -65,11 +78,24 @@ module GrpcKit
65
78
 
66
79
  private
67
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
+
68
94
  def build_headers(metadata: {}, timeout: nil, **headers)
69
95
  hdrs = metadata.merge(headers).merge(
70
96
  ':method' => 'POST',
71
97
  ':scheme' => 'http',
72
- ':path' => @path,
98
+ ':path' => @config.path,
73
99
  ':authority' => @authority,
74
100
  'te' => 'trailers',
75
101
  'content-type' => 'application/grpc',
@@ -7,7 +7,7 @@ module GrpcKit
7
7
  # @params compress [Boolean]
8
8
  def pack(data, compress = false)
9
9
  c = compress ? 1 : 0
10
- [c, data.size, data].pack('CNa*')
10
+ [c, data.bytesize, data].pack('CNa*')
11
11
  end
12
12
 
13
13
  # @params data [String]
@@ -51,6 +51,7 @@ module GrpcKit
51
51
  def read
52
52
  metadata = @data.slice!(0, METADATA_SIZE)
53
53
  c, size = metadata.unpack('CN')
54
+ # TODO: more efficient code
54
55
  data = @data.slice!(0, size)
55
56
  [c != 0, size, data]
56
57
  end
@@ -3,6 +3,7 @@
3
3
  require 'forwardable'
4
4
 
5
5
  require 'grpc_kit/stream'
6
+ require 'grpc_kit/status_codes'
6
7
 
7
8
  module GrpcKit
8
9
  module Streams
@@ -11,24 +12,41 @@ module GrpcKit
11
12
 
12
13
  delegate %i[each recv] => :@stream
13
14
 
14
- def initialize(protobuf:, session:, stream:)
15
- @protobuf = protobuf
16
- @session = session
17
- @stream = GrpcKit::Stream.new(protobuf: @protobuf, session: @session, stream: stream)
15
+ def initialize(stream:, session:, config:)
16
+ @stream = GrpcKit::Stream.new(protobuf: config.protobuf, session: session, stream: stream)
17
+ @config = config
18
18
  @sent_first_msg = false
19
19
  end
20
20
 
21
21
  def send_msg(data, last: false)
22
- @stream.send(data, last: last)
22
+ if last
23
+ @stream.send_trailer # TODO: pass trailer metadata
24
+ end
23
25
 
26
+ @stream.send(data, last: last, limit_size: @config.max_send_message_size)
24
27
  return if @sent_first_msg
25
28
 
26
- @session.submit_response(
27
- @stream.stream_id,
28
- ':status' => '200',
29
- 'content-type' => 'application/grpc',
30
- 'accept-encoding' => 'identity',
31
- )
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)
32
50
  @sent_first_msg = true
33
51
  end
34
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrpcKit
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grpc_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - ganmacs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-19 00:00:00.000000000 Z
11
+ date: 2018-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ds9
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: grpc-tools
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 1.15.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 1.15.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: pry-byebug
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -152,7 +152,9 @@ files:
152
152
  - LICENSE.txt
153
153
  - README.md
154
154
  - Rakefile
155
+ - TODO.md
155
156
  - bin/console
157
+ - bin/nghttp2
156
158
  - bin/setup
157
159
  - examples/helloworld/helloworld.proto
158
160
  - examples/helloworld/helloworld_pb.rb
@@ -190,6 +192,7 @@ files:
190
192
  - lib/grpc_kit/rpcs/bidi_streamer.rb
191
193
  - lib/grpc_kit/rpcs/call.rb
192
194
  - lib/grpc_kit/rpcs/client_streamer.rb
195
+ - lib/grpc_kit/rpcs/error.rb
193
196
  - lib/grpc_kit/rpcs/request_response.rb
194
197
  - lib/grpc_kit/rpcs/server_streamer.rb
195
198
  - lib/grpc_kit/server.rb
@@ -200,13 +203,13 @@ files:
200
203
  - lib/grpc_kit/session/io.rb
201
204
  - lib/grpc_kit/session/server.rb
202
205
  - lib/grpc_kit/session/stream.rb
206
+ - lib/grpc_kit/session/stream_status.rb
203
207
  - lib/grpc_kit/status_codes.rb
204
208
  - lib/grpc_kit/stream.rb
205
209
  - lib/grpc_kit/streams/client.rb
206
210
  - lib/grpc_kit/streams/packable.rb
207
211
  - lib/grpc_kit/streams/send_buffer.rb
208
212
  - lib/grpc_kit/streams/server.rb
209
- - lib/grpc_kit/streams/stream.rb
210
213
  - lib/grpc_kit/version.rb
211
214
  homepage: https://github.com/ganmacs/grpc_kit
212
215
  licenses: