grpc_kit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +70 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/helloworld/helloworld.proto +37 -0
- data/examples/helloworld/helloworld_pb.rb +18 -0
- data/examples/helloworld/helloworld_services_pb.rb +22 -0
- data/examples/helloworld_client.rb +12 -0
- data/examples/helloworld_server.rb +25 -0
- data/examples/routeguide/routeguide.json +601 -0
- data/examples/routeguide/routeguide.proto +110 -0
- data/examples/routeguide/routeguide_pb.rb +37 -0
- data/examples/routeguide/routeguide_services_pb.rb +61 -0
- data/examples/routeguide_client.rb +23 -0
- data/examples/routeguide_server.rb +35 -0
- data/grpc_kit.gemspec +35 -0
- data/lib/grpc.rb +7 -0
- data/lib/grpc_kit.rb +17 -0
- data/lib/grpc_kit/client.rb +107 -0
- data/lib/grpc_kit/errors.rb +25 -0
- data/lib/grpc_kit/grpc/dsl.rb +87 -0
- data/lib/grpc_kit/grpc/generic_service.rb +26 -0
- data/lib/grpc_kit/grpc/stream.rb +22 -0
- data/lib/grpc_kit/io/basic.rb +35 -0
- data/lib/grpc_kit/rpc_desc.rb +72 -0
- data/lib/grpc_kit/server.rb +76 -0
- data/lib/grpc_kit/session/client.rb +107 -0
- data/lib/grpc_kit/session/server.rb +155 -0
- data/lib/grpc_kit/session/stream.rb +68 -0
- data/lib/grpc_kit/status_codes.rb +24 -0
- data/lib/grpc_kit/version.rb +5 -0
- metadata +208 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
// Copyright 2015 gRPC authors.
|
2
|
+
//
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
// you may not use this file except in compliance with the License.
|
5
|
+
// You may obtain a copy of the License at
|
6
|
+
//
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
//
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
// See the License for the specific language governing permissions and
|
13
|
+
// limitations under the License.
|
14
|
+
|
15
|
+
syntax = "proto3";
|
16
|
+
|
17
|
+
option java_multiple_files = true;
|
18
|
+
option java_package = "io.grpc.examples.routeguide";
|
19
|
+
option java_outer_classname = "RouteGuideProto";
|
20
|
+
|
21
|
+
package routeguide;
|
22
|
+
|
23
|
+
// Interface exported by the server.
|
24
|
+
service RouteGuide {
|
25
|
+
// A simple RPC.
|
26
|
+
//
|
27
|
+
// Obtains the feature at a given position.
|
28
|
+
//
|
29
|
+
// A feature with an empty name is returned if there's no feature at the given
|
30
|
+
// position.
|
31
|
+
rpc GetFeature(Point) returns (Feature) {}
|
32
|
+
|
33
|
+
// A server-to-client streaming RPC.
|
34
|
+
//
|
35
|
+
// Obtains the Features available within the given Rectangle. Results are
|
36
|
+
// streamed rather than returned at once (e.g. in a response message with a
|
37
|
+
// repeated field), as the rectangle may cover a large area and contain a
|
38
|
+
// huge number of features.
|
39
|
+
rpc ListFeatures(Rectangle) returns (stream Feature) {}
|
40
|
+
|
41
|
+
// A client-to-server streaming RPC.
|
42
|
+
//
|
43
|
+
// Accepts a stream of Points on a route being traversed, returning a
|
44
|
+
// RouteSummary when traversal is completed.
|
45
|
+
rpc RecordRoute(stream Point) returns (RouteSummary) {}
|
46
|
+
|
47
|
+
// A Bidirectional streaming RPC.
|
48
|
+
//
|
49
|
+
// Accepts a stream of RouteNotes sent while a route is being traversed,
|
50
|
+
// while receiving other RouteNotes (e.g. from other users).
|
51
|
+
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
|
52
|
+
}
|
53
|
+
|
54
|
+
// Points are represented as latitude-longitude pairs in the E7 representation
|
55
|
+
// (degrees multiplied by 10**7 and rounded to the nearest integer).
|
56
|
+
// Latitudes should be in the range +/- 90 degrees and longitude should be in
|
57
|
+
// the range +/- 180 degrees (inclusive).
|
58
|
+
message Point {
|
59
|
+
int32 latitude = 1;
|
60
|
+
int32 longitude = 2;
|
61
|
+
}
|
62
|
+
|
63
|
+
// A latitude-longitude rectangle, represented as two diagonally opposite
|
64
|
+
// points "lo" and "hi".
|
65
|
+
message Rectangle {
|
66
|
+
// One corner of the rectangle.
|
67
|
+
Point lo = 1;
|
68
|
+
|
69
|
+
// The other corner of the rectangle.
|
70
|
+
Point hi = 2;
|
71
|
+
}
|
72
|
+
|
73
|
+
// A feature names something at a given point.
|
74
|
+
//
|
75
|
+
// If a feature could not be named, the name is empty.
|
76
|
+
message Feature {
|
77
|
+
// The name of the feature.
|
78
|
+
string name = 1;
|
79
|
+
|
80
|
+
// The point where the feature is detected.
|
81
|
+
Point location = 2;
|
82
|
+
}
|
83
|
+
|
84
|
+
// A RouteNote is a message sent while at a given point.
|
85
|
+
message RouteNote {
|
86
|
+
// The location from which the message is sent.
|
87
|
+
Point location = 1;
|
88
|
+
|
89
|
+
// The message to be sent.
|
90
|
+
string message = 2;
|
91
|
+
}
|
92
|
+
|
93
|
+
// A RouteSummary is received in response to a RecordRoute rpc.
|
94
|
+
//
|
95
|
+
// It contains the number of individual points received, the number of
|
96
|
+
// detected features, and the total distance covered as the cumulative sum of
|
97
|
+
// the distance between each point.
|
98
|
+
message RouteSummary {
|
99
|
+
// The number of points received.
|
100
|
+
int32 point_count = 1;
|
101
|
+
|
102
|
+
// The number of known features passed while traversing the route.
|
103
|
+
int32 feature_count = 2;
|
104
|
+
|
105
|
+
// The distance covered in metres.
|
106
|
+
int32 distance = 3;
|
107
|
+
|
108
|
+
// The duration of the traversal in seconds.
|
109
|
+
int32 elapsed_time = 4;
|
110
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# source: routeguide.proto
|
3
|
+
|
4
|
+
require 'google/protobuf'
|
5
|
+
|
6
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
+
add_message "routeguide.Point" do
|
8
|
+
optional :latitude, :int32, 1
|
9
|
+
optional :longitude, :int32, 2
|
10
|
+
end
|
11
|
+
add_message "routeguide.Rectangle" do
|
12
|
+
optional :lo, :message, 1, "routeguide.Point"
|
13
|
+
optional :hi, :message, 2, "routeguide.Point"
|
14
|
+
end
|
15
|
+
add_message "routeguide.Feature" do
|
16
|
+
optional :name, :string, 1
|
17
|
+
optional :location, :message, 2, "routeguide.Point"
|
18
|
+
end
|
19
|
+
add_message "routeguide.RouteNote" do
|
20
|
+
optional :location, :message, 1, "routeguide.Point"
|
21
|
+
optional :message, :string, 2
|
22
|
+
end
|
23
|
+
add_message "routeguide.RouteSummary" do
|
24
|
+
optional :point_count, :int32, 1
|
25
|
+
optional :feature_count, :int32, 2
|
26
|
+
optional :distance, :int32, 3
|
27
|
+
optional :elapsed_time, :int32, 4
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Routeguide
|
32
|
+
Point = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Point").msgclass
|
33
|
+
Rectangle = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Rectangle").msgclass
|
34
|
+
Feature = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.Feature").msgclass
|
35
|
+
RouteNote = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.RouteNote").msgclass
|
36
|
+
RouteSummary = Google::Protobuf::DescriptorPool.generated_pool.lookup("routeguide.RouteSummary").msgclass
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
+
# Source: routeguide.proto for package 'routeguide'
|
3
|
+
# Original file comments:
|
4
|
+
# Copyright 2015 gRPC authors.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'grpc'
|
20
|
+
require 'routeguide_pb'
|
21
|
+
|
22
|
+
module Routeguide
|
23
|
+
module RouteGuide
|
24
|
+
# Interface exported by the server.
|
25
|
+
class Service
|
26
|
+
|
27
|
+
include GRPC::GenericService
|
28
|
+
|
29
|
+
self.marshal_class_method = :encode
|
30
|
+
self.unmarshal_class_method = :decode
|
31
|
+
self.service_name = 'routeguide.RouteGuide'
|
32
|
+
|
33
|
+
# A simple RPC.
|
34
|
+
#
|
35
|
+
# Obtains the feature at a given position.
|
36
|
+
#
|
37
|
+
# A feature with an empty name is returned if there's no feature at the given
|
38
|
+
# position.
|
39
|
+
rpc :GetFeature, Point, Feature
|
40
|
+
# A server-to-client streaming RPC.
|
41
|
+
#
|
42
|
+
# Obtains the Features available within the given Rectangle. Results are
|
43
|
+
# streamed rather than returned at once (e.g. in a response message with a
|
44
|
+
# repeated field), as the rectangle may cover a large area and contain a
|
45
|
+
# huge number of features.
|
46
|
+
rpc :ListFeatures, Rectangle, stream(Feature)
|
47
|
+
# A client-to-server streaming RPC.
|
48
|
+
#
|
49
|
+
# Accepts a stream of Points on a route being traversed, returning a
|
50
|
+
# RouteSummary when traversal is completed.
|
51
|
+
rpc :RecordRoute, stream(Point), RouteSummary
|
52
|
+
# A Bidirectional streaming RPC.
|
53
|
+
#
|
54
|
+
# Accepts a stream of RouteNotes sent while a route is being traversed,
|
55
|
+
# while receiving other RouteNotes (e.g. from other users).
|
56
|
+
rpc :RouteChat, stream(RouteNote), stream(RouteNote)
|
57
|
+
end
|
58
|
+
|
59
|
+
Stub = Service.rpc_stub_class
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('./examples/routeguide')
|
4
|
+
|
5
|
+
require 'grpc_kit'
|
6
|
+
require 'pry'
|
7
|
+
require 'routeguide_services_pb'
|
8
|
+
|
9
|
+
stub = Routeguide::RouteGuide::Stub.new('localhost', 50051)
|
10
|
+
|
11
|
+
def get_feature(stub)
|
12
|
+
points = [
|
13
|
+
Routeguide::Point.new(latitude: 409_146_138, longitude: -746_188_906),
|
14
|
+
Routeguide::Point.new(latitude: 0, longitude: 0)
|
15
|
+
]
|
16
|
+
|
17
|
+
points.each do |pt|
|
18
|
+
feature = stub.get_feature(pt)
|
19
|
+
puts "get_feature #{feature.name}, #{feature.location}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
get_feature(stub)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('./examples/routeguide')
|
4
|
+
|
5
|
+
require 'grpc_kit'
|
6
|
+
require 'pry'
|
7
|
+
require 'json'
|
8
|
+
require 'routeguide_services_pb'
|
9
|
+
|
10
|
+
class Server < Routeguide::RouteGuide::Service
|
11
|
+
RESOURCE_PATH = './examples/routeguide/routeguide.json'
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
File.open(RESOURCE_PATH) do |f|
|
15
|
+
features = JSON.load(f.read)
|
16
|
+
@features = Hash[features.map { |x| [x['location'], x['name']] }]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_feature(point, _call)
|
21
|
+
name = @features.fetch({ 'longitude' => point.longitude, 'latitude' => point.latitude }, '')
|
22
|
+
Routeguide::Feature.new(location: point, name: name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sock = TCPServer.new(50051)
|
27
|
+
|
28
|
+
server = GrpcKit::Server.new
|
29
|
+
server.handle(Server.new)
|
30
|
+
server.run
|
31
|
+
|
32
|
+
loop do
|
33
|
+
conn = sock.accept
|
34
|
+
server.session_start(conn)
|
35
|
+
end
|
data/grpc_kit.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'grpc_kit/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'grpc_kit'
|
9
|
+
spec.version = GrpcKit::VERSION
|
10
|
+
spec.authors = ['ganmacs']
|
11
|
+
spec.email = ['ganmacs@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'A kit for creating gRPC server/client'
|
14
|
+
spec.description = 'A kit for creating gRPC server/client'
|
15
|
+
spec.homepage = 'https://github.com/ganmacs/grpc_kit'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_dependency 'ds9', '~> 1.1.1'
|
26
|
+
spec.add_dependency 'google-protobuf', '~> 3.6.1'
|
27
|
+
spec.add_dependency 'googleapis-common-protos-types', '~> 1.0.2'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler'
|
30
|
+
spec.add_development_dependency 'grpc-tools'
|
31
|
+
spec.add_development_dependency 'pry-byebug'
|
32
|
+
spec.add_development_dependency 'rake'
|
33
|
+
spec.add_development_dependency 'rspec'
|
34
|
+
spec.add_development_dependency 'rubocop'
|
35
|
+
end
|
data/lib/grpc.rb
ADDED
data/lib/grpc_kit.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
require 'grpc_kit/version'
|
6
|
+
require 'grpc_kit/server'
|
7
|
+
require 'grpc_kit/client'
|
8
|
+
|
9
|
+
module GrpcKit
|
10
|
+
def self.logger
|
11
|
+
@logger ||= Logger.new(STDOUT, level: :debug) # TODO: use :info level
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.logger=(logger)
|
15
|
+
@logger = logger
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'grpc_kit/session/client'
|
5
|
+
|
6
|
+
module GrpcKit
|
7
|
+
class Client
|
8
|
+
def initialize(host, port, io = GrpcKit::IO::Basic)
|
9
|
+
@host = host
|
10
|
+
@port = port
|
11
|
+
@authority = "#{host}:#{port}"
|
12
|
+
@io = io
|
13
|
+
end
|
14
|
+
|
15
|
+
# @params handler [object]
|
16
|
+
def handle(handler)
|
17
|
+
klass = handler.class
|
18
|
+
|
19
|
+
klass.rpc_descs.values.each do |rpc_desc|
|
20
|
+
path = rpc_desc.path(klass.service_name)
|
21
|
+
if @rpc_descs[path]
|
22
|
+
raise "Duplicated method registered #{key}, class: #{handler}"
|
23
|
+
end
|
24
|
+
|
25
|
+
@rpc_descs[path] = [rpc_desc, handler]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class RequestResponse
|
30
|
+
attr_writer :session
|
31
|
+
|
32
|
+
def initialize(path, opts = {})
|
33
|
+
@path = path
|
34
|
+
@opts = opts
|
35
|
+
@session = nil
|
36
|
+
@data = ''
|
37
|
+
end
|
38
|
+
|
39
|
+
def invoke(data)
|
40
|
+
@session.submit_settings([])
|
41
|
+
|
42
|
+
stream_id = submit_request(data)
|
43
|
+
@session.start(stream_id)
|
44
|
+
@data
|
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
|
+
@data << buf
|
55
|
+
stream.recv2(buf)
|
56
|
+
else
|
57
|
+
raise 'not supported'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def submit_request(data)
|
64
|
+
@session.submit_request(
|
65
|
+
{
|
66
|
+
':method' => 'POST',
|
67
|
+
':scheme' => 'http',
|
68
|
+
':authority' => @opts[:authority],
|
69
|
+
':path' => @path.to_s,
|
70
|
+
'te' => 'trailers',
|
71
|
+
'content-type' => 'application/grpc',
|
72
|
+
'user-agent' => "grpc-ruby/#{GrpcKit::VERSION} (grpc_kit)",
|
73
|
+
'grpc-accept-encoding' => 'identity,deflate,gzip',
|
74
|
+
},
|
75
|
+
data,
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def request_response(path, request, rpc_desc, opts = {})
|
81
|
+
GrpcKit.logger.info('Calling request_respose')
|
82
|
+
sock = TCPSocket.new(@host, @port)
|
83
|
+
|
84
|
+
rr = RequestResponse.new(path, { authority: @authority }.merge(opts))
|
85
|
+
session = GrpcKit::Session::Client.new(@io.new(sock, sock), rr)
|
86
|
+
rr.session = session
|
87
|
+
|
88
|
+
req = rpc_desc.encode2(request)
|
89
|
+
data = [0, req.length, req].pack('CNa*')
|
90
|
+
|
91
|
+
resp = rr.invoke(data)
|
92
|
+
rpc_desc.decode2(resp)
|
93
|
+
end
|
94
|
+
|
95
|
+
def client_streamer(path, requests, rpc, opts = {})
|
96
|
+
GrpcKit.logger.info('Calling client_streamer')
|
97
|
+
end
|
98
|
+
|
99
|
+
def server_streamer(path, request, metadata, opts = {})
|
100
|
+
GrpcKit.logger.info('Calling server_streamer')
|
101
|
+
end
|
102
|
+
|
103
|
+
def bidi_streamer(path, requests, metadata, opts = {})
|
104
|
+
GrpcKit.logger.info('Calling bidi_streamer')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'grpc_kit/status_codes'
|
4
|
+
|
5
|
+
module GrpcKit
|
6
|
+
module Errors
|
7
|
+
# https://github.com/grpc/grpc/blob/23b5b1a5a9c7084c5b64d4998ee15af0f77bd589/doc/statuscodes.md
|
8
|
+
class BadStatus < StandardError
|
9
|
+
def initialize(code, message)
|
10
|
+
super("#{code}:#{details}")
|
11
|
+
@code = code
|
12
|
+
@message = message
|
13
|
+
end
|
14
|
+
|
15
|
+
class Unimplemented < BadStatus
|
16
|
+
def initialize(name)
|
17
|
+
super(
|
18
|
+
GrpcKit::StatusCodes::UNIMPLEMENTED,
|
19
|
+
"Method not found at server: #{name}"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|