grpc_kit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/errors'
4
+ require 'grpc_kit/rpc_desc'
5
+ require 'grpc_kit/client'
6
+ require 'grpc_kit/grpc/stream'
7
+
8
+ module GrpcKit
9
+ module GRPC
10
+ module Dsl
11
+ attr_accessor :service_name
12
+
13
+ attr_writer :marshal_class_method, :unmarshal_class_method
14
+
15
+ def inherited(subclass)
16
+ subclass.rpc_descs.merge!(rpc_descs)
17
+ subclass.service_name = service_name
18
+ end
19
+
20
+ def rpc(name, input, output)
21
+ if rpc_descs.key?(name)
22
+ raise "rpc (#{name}) is already defined"
23
+ end
24
+
25
+ unless input.respond_to?(@marshal_class_method)
26
+ raise "#{marshal} must implement #{marshal}.#{@marshal_class_method}"
27
+ end
28
+
29
+ unless output.respond_to?(@unmarshal_class_method)
30
+ raise "#{unmarshal} must implement #{unmarshal}.#{@unmarshal_class_method}"
31
+ end
32
+
33
+ # create StreamDesc?
34
+ rpc_descs[name] = GrpcKit::RpcDesc.new(
35
+ name: name,
36
+ input: input,
37
+ output: output,
38
+ marshal_method: @marshal_class_method,
39
+ unmarshal_method: @unmarshal_class_method,
40
+ )
41
+
42
+ define_method(rpc_descs[name].ruby_style_name) do |_, _|
43
+ raise GrpcKit::Errors::Unimplemented, name.to_s
44
+ end
45
+ end
46
+
47
+ def stream(cls)
48
+ GrpcKit::GRPC::Stream.new(cls)
49
+ end
50
+
51
+ def rpc_stub_class
52
+ rpc_descs_ = rpc_descs
53
+ service_name_ = service_name
54
+ Class.new(GrpcKit::Client) do
55
+ rpc_descs_.each_pair do |_, rpc_desc|
56
+ method_name = rpc_desc.ruby_style_name
57
+ path = rpc_desc.path(service_name_)
58
+
59
+ if rpc_desc.request_response?
60
+ define_method(method_name) do |request, opts = {}|
61
+ request_response(path, request, rpc_desc, opts)
62
+ end
63
+ elsif rpc_desc.client_streamer?
64
+ define_method(method_name) do |requests, opts = {}|
65
+ client_streamer(path, requests, rpc_desc, opts)
66
+ end
67
+ elsif rpc_desc.server_streamer?
68
+ define_method(method_name) do |request, opts = {}, &blk|
69
+ server_streamer(path, request, rpc_desc, opts, &blk)
70
+ end
71
+ elsif rpc_desc.bidi_streamer?
72
+ define_method(method_name) do |requests, opts = {}, &blk|
73
+ bidi_streamer(path, requests, rpc_desc, opts, &blk)
74
+ end
75
+ else
76
+ raise "unknown #{rpc_desc}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def rpc_descs
83
+ @rpc_descs ||= {}
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/grpc/dsl'
4
+
5
+ module GrpcKit
6
+ module GRPC
7
+ module GenericService
8
+ def self.included(obj)
9
+ obj.extend(GrpcKit::GRPC::Dsl)
10
+ return unless obj.service_name.nil?
11
+
12
+ # if obj.name.nil?
13
+ # obj.service_name = 'GenericService'
14
+ # else
15
+ # modules = obj.name.split('::')
16
+ # obj.service_name =
17
+ # if modules.length > 2
18
+ # modules[modules.length - 2]
19
+ # else
20
+ # modules.first
21
+ # end
22
+ # end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/grpc/dsl'
4
+ require 'forwardable'
5
+
6
+ module GrpcKit
7
+ module GRPC
8
+ class Stream
9
+ extend Forwardable
10
+ delegate %i[encode decode] => :@klass
11
+
12
+ def initialize(klass)
13
+ @klass = klass
14
+ end
15
+
16
+ # FIXME: Do not use method_missing...
17
+ def method_missing(name, *args, &block)
18
+ @klass.send(name, *args, &block)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ds9'
4
+
5
+ module GrpcKit
6
+ module IO
7
+ class Basic
8
+ def initialize(reader, writer)
9
+ @reader = reader
10
+ @writer = writer
11
+ end
12
+
13
+ def read(length)
14
+ data = @reader.read_nonblock(length, nil, exception: false)
15
+
16
+ case data
17
+ when :wait_readable
18
+ DS9::ERR_WOULDBLOCK
19
+ when nil
20
+ ''
21
+ else
22
+ data
23
+ end
24
+ end
25
+
26
+ def write(data)
27
+ @writer.write(data)
28
+ end
29
+
30
+ def wait_readable
31
+ @reader.wait_writable
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/grpc/stream'
4
+
5
+ module GrpcKit
6
+ class RpcDesc
7
+ def initialize(name:, input:, output:, marshal_method:, unmarshal_method:)
8
+ @name = name
9
+ @input = input
10
+ @output = output
11
+ @marshal_method = marshal_method
12
+ @unmarshal_method = unmarshal_method
13
+ end
14
+
15
+ def encode(val)
16
+ @output.send(@marshal_method, val)
17
+ end
18
+
19
+ def decode(val)
20
+ @input.send(@unmarshal_method, val)
21
+ end
22
+
23
+ def encode2(val)
24
+ @input.send(@marshal_method, val)
25
+ end
26
+
27
+ def decode2(val)
28
+ @output.send(@unmarshal_method, val)
29
+ end
30
+
31
+ def invoke(rpc, val)
32
+ args = decode(val)
33
+ ret = rpc.send(to_underscore(@name), args, nil) # nil is GRPC::Call object
34
+ encode(ret)
35
+ end
36
+
37
+ def ruby_style_name
38
+ @ruby_style_name ||= to_underscore(@name).to_sym
39
+ end
40
+
41
+ def path(service_name)
42
+ "/#{service_name}/#{@name}".to_sym
43
+ end
44
+
45
+ def request_response?
46
+ !@input.is_a?(GrpcKit::GRPC::Stream) && !@output.is_a?(GrpcKit::GRPC::Stream)
47
+ end
48
+
49
+ def client_streamer?
50
+ @input.is_a?(GrpcKit::GRPC::Stream) && !@output.is_a?(GrpcKit::GRPC::Stream)
51
+ end
52
+
53
+ def server_streamer?
54
+ !@input.is_a?(GrpcKit::GRPC::Stream) && @output.is_a?(GrpcKit::GRPC::Stream)
55
+ end
56
+
57
+ def bidi_streamer?
58
+ @input.is_a?(GrpcKit::GRPC::Stream) && @output.is_a?(GrpcKit::GRPC::Stream)
59
+ end
60
+
61
+ private
62
+
63
+ def to_underscore(val)
64
+ val
65
+ .to_s
66
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
67
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
68
+ .tr('-', '_')
69
+ .downcase
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc_kit/io/basic'
4
+ require 'grpc_kit/session/server'
5
+
6
+ module GrpcKit
7
+ class Server
8
+ def initialize
9
+ @sessions = []
10
+ @handler = {}
11
+ @rpc_descs = {}
12
+ end
13
+
14
+ # @params handler [object]
15
+ def handle(handler)
16
+ klass = handler.class
17
+
18
+ klass.rpc_descs.values.each do |rpc_desc|
19
+ path = rpc_desc.path(klass.service_name)
20
+ if @rpc_descs[path]
21
+ raise "Duplicated method registered #{key}, class: #{handler}"
22
+ end
23
+
24
+ @rpc_descs[path] = [rpc_desc, handler]
25
+ end
26
+ end
27
+
28
+ def run
29
+ GrpcKit.logger.info("Start grpc_kit v#{GrpcKit::VERSION}")
30
+ # XXX
31
+ end
32
+
33
+ def stop
34
+ GrpcKit.logger.info('Stop grpc_kit')
35
+
36
+ @sessions.each(&:stop)
37
+ end
38
+
39
+ def session_start(conn, io = GrpcKit::IO::Basic)
40
+ session = GrpcKit::Session::Server.new(io.new(conn, conn), self) # TODO: change self to proper object
41
+ @sessions << session
42
+
43
+ session.submit_settings([])
44
+ session.start # blocking
45
+ end
46
+
47
+ def on_data_chunk_recv(stream, data)
48
+ compressed, length, buf = data.unpack('CNa*')
49
+ if compressed == 0 # TODO: not
50
+ if length != buf.size
51
+ raise 'recived data inconsistent'
52
+ end
53
+
54
+ stream.recv(buf)
55
+ else
56
+ raise 'not supported'
57
+ end
58
+ end
59
+
60
+ def on_frame_data_recv(stream)
61
+ return unless stream.exist_data?
62
+
63
+ path = stream.headers[':path']
64
+ rpc = @rpc_descs[path.to_sym]
65
+ if rpc
66
+ resp = rpc[0].invoke(rpc[1], stream.data)
67
+ buf = [0, resp.length, resp].pack('CNa*')
68
+ stream.data = ''
69
+ stream.send(StringIO.new(buf))
70
+ else
71
+ # TODO: 404
72
+ raise "unkown path #{path}"
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ds9'
4
+ require 'grpc_kit/session/stream'
5
+
6
+ module GrpcKit
7
+ module Session
8
+ class Client < DS9::Client
9
+ # @io [GrpcKit::IO::XXX]
10
+ def initialize(io, handler)
11
+ super() # initialize DS9::Session
12
+
13
+ @io = io
14
+ @streams = {}
15
+ @handler = handler
16
+ end
17
+
18
+ def start(stream_id)
19
+ stream = GrpcKit::Session::Stream.new(stream_id: stream_id)
20
+ @streams[stream_id] = stream
21
+
22
+ while want_read? || want_write?
23
+ if stream.closed?
24
+ break
25
+ elsif !stream.exist_data?
26
+ receive
27
+
28
+ send
29
+ else
30
+ break
31
+ # GrpcKit.logger.info("unknown #{stream}")
32
+ end
33
+ end
34
+ # invalid if receive and send are not called
35
+ end
36
+
37
+ private
38
+
39
+ # for nghttp2_session_callbacks_set_send_callback
40
+ # override
41
+ def send_event(string)
42
+ @io.write(string)
43
+ end
44
+
45
+ # for nghttp2_session_callbacks_set_recv_callback
46
+ # override
47
+ def recv_event(length)
48
+ @io.read(length)
49
+ end
50
+
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)
54
+ end
55
+
56
+ # provider for nghttp2_submit_response
57
+ # def on_data_source_read(stream_id, length)
58
+ # end
59
+
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
64
+
65
+ # # for nghttp2_session_callbacks_set_on_frame_not_send_callback
66
+ # def on_frame_not_send(frame, reason)
67
+ # end
68
+
69
+ # # for nghttp2_session_callbacks_set_on_frame_send_callback
70
+ # def on_frame_send(frame, reason)
71
+ # end
72
+
73
+ # # for nghttp2_session_callbacks_set_on_header_callback
74
+ # def on_header(name, value, frame, flags)
75
+ # end
76
+
77
+ # # for nghttp2_session_callbacks_set_on_begin_headers_callback
78
+ # def on_begin_header(name, value, frame, flags)
79
+ # end
80
+
81
+ # # for nghttp2_session_callbacks_set_on_begin_frame_callback
82
+ # def on_begin_frame(frame_header)
83
+ # end
84
+
85
+ # # for nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
86
+ # def on_invalid_frame_recv(frame, error_code)
87
+ # 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
+ end
106
+ end
107
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ds9'
4
+ require 'grpc_kit/session/stream'
5
+
6
+ module GrpcKit
7
+ module Session
8
+ class Server < DS9::Server
9
+ # @io [GrpcKit::IO::XXX]
10
+ def initialize(io, handler)
11
+ super() # initialize DS9::Server
12
+
13
+ @io = io
14
+ @streams = {}
15
+ @stop = false
16
+ @handler = handler
17
+ end
18
+
19
+ def start
20
+ @io.wait_readable
21
+ until @stop && (want_read? || want_write?)
22
+ if want_write?
23
+ send
24
+ end
25
+
26
+ if want_read?
27
+ receive
28
+ end
29
+ end
30
+ end
31
+
32
+ def stop
33
+ @stop = true
34
+ end
35
+
36
+ private
37
+
38
+ # provider for nghttp2_submit_response
39
+ # override
40
+ def on_data_source_read(stream_id, length)
41
+ GrpcKit.logger.debug("on_data_source_read #{stream_id}, lenght=#{length}")
42
+
43
+ data = @streams[stream_id].read(length)
44
+ if data.nil?
45
+ submit_trailer(stream_id, 'grpc-status' => '0')
46
+ false # nil mean END_STREAM
47
+ else
48
+ data
49
+ end
50
+ end
51
+
52
+ # for nghttp2_session_callbacks_set_send_callback
53
+ # override
54
+ def send_event(string)
55
+ GrpcKit.logger.debug('send_event')
56
+
57
+ @io.write(string)
58
+ end
59
+
60
+ # for nghttp2_session_callbacks_set_recv_callback
61
+ # override
62
+ def recv_event(length)
63
+ # GrpcKit.logger.debug("recv_event #{length}")
64
+ @io.read(length)
65
+ end
66
+
67
+ # for nghttp2_session_callbacks_set_on_frame_send_callback
68
+ def on_frame_recv(frame)
69
+ GrpcKit.logger.debug("on_frame_recv #{frame}")
70
+ case frame
71
+ when DS9::Frames::Data
72
+ # need to port NGHTTP2_FLAG_END_STREAM to check frame.flag has it
73
+ stream = @streams[frame.stream_id]
74
+ resp = @handler.on_frame_data_recv(stream)
75
+ unless resp
76
+ return # TODO
77
+ end
78
+
79
+ submit_response(
80
+ frame.stream_id,
81
+ ':status' => '200',
82
+ 'content-type' => 'application/grpc',
83
+ 'accept-encoding' => 'identity,gzip',
84
+ )
85
+ # when DS9::Frames::Headers
86
+ # need to port NGHTTP2_FLAG_END_STREAM to check frame.flag has it
87
+ # when DS9::Frames::Goaway
88
+ # when DS9::Frames::RstStream
89
+ else
90
+ GrpcKit.logger.info("unsupport frame #{frame}")
91
+ end
92
+
93
+ true
94
+ end
95
+
96
+ # for nghttp2_session_callbacks_set_on_frame_send_callback
97
+ def on_frame_send(frame)
98
+ GrpcKit.logger.debug("on_frame_send #{frame}")
99
+ true
100
+ end
101
+
102
+ # for nghttp2_session_callbacks_set_on_frame_not_send_callback
103
+ def on_frame_not_send(frame, reason)
104
+ GrpcKit.logger.debug("on_frame_not_send frame=#{frame}, reason=#{reason}")
105
+ true
106
+ end
107
+
108
+ # for nghttp2_session_callbacks_set_on_begin_headers_callback
109
+ def on_begin_headers(header)
110
+ stream_id = header.stream_id
111
+ GrpcKit.logger.debug("on_begin_header stream_id=#{stream_id}")
112
+
113
+ if @streams[stream_id]
114
+ raise "#{stream_id} is already existed"
115
+ end
116
+
117
+ @streams[stream_id] = GrpcKit::Session::Stream.new(stream_id: stream_id)
118
+ end
119
+
120
+ # for nghttp2_session_callbacks_set_on_header_callback
121
+ def on_header(name, value, frame, _flags)
122
+ @streams[frame.stream_id].headers[name] = value
123
+ end
124
+
125
+ # for nghttp2_session_callbacks_set_on_begin_frame_callback
126
+ def on_begin_frame(frame_header)
127
+ GrpcKit.logger.debug("on_begin_frame #{frame_header}")
128
+ true
129
+ end
130
+
131
+ # for nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
132
+ def on_invalid_frame_recv(frame, error_code)
133
+ GrpcKit.logger.debug("on_invalid_frame_recv #{error_code}")
134
+ true
135
+ end
136
+
137
+ # for nghttp2_session_callbacks_set_on_stream_close_callback
138
+ def on_stream_close(stream_id, error_code)
139
+ GrpcKit.logger.debug("on_stream_close stream_id=#{stream_id}, error_code=#{error_code}")
140
+ @streams.delete(stream_id)
141
+ end
142
+
143
+ # for nghttp2_session_callbacks_set_on_data_chunk_recv_callback
144
+ def on_data_chunk_recv(stream_id, data, _flags)
145
+ @handler.on_data_chunk_recv(@streams[stream_id], data)
146
+ end
147
+
148
+ # nghttp2_session_callbacks_set_before_frame_send_callback
149
+ def before_frame_send(frame)
150
+ GrpcKit.logger.debug("before_frame_send frame=#{frame}")
151
+ true
152
+ end
153
+ end
154
+ end
155
+ end