grpc-server-reflection 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +39 -0
- data/grpc/reflection/v1/reflection.proto +146 -0
- data/grpc/reflection/v1/reflection_pb.rb +26 -0
- data/grpc/reflection/v1/reflection_services_pb.rb +50 -0
- data/grpc/reflection/v1alpha/reflection.proto +136 -0
- data/grpc/reflection/v1alpha/reflection_pb.rb +26 -0
- data/grpc/reflection/v1alpha/reflection_services_pb.rb +45 -0
- data/lib/grpc_server_reflection/descriptor_registry/dependency_resolver.rb +42 -0
- data/lib/grpc_server_reflection/descriptor_registry/file_descriptor_indexer.rb +68 -0
- data/lib/grpc_server_reflection/descriptor_registry/object_space_indexer.rb +153 -0
- data/lib/grpc_server_reflection/descriptor_registry/proto_builder.rb +259 -0
- data/lib/grpc_server_reflection/descriptor_registry/type_mapping.rb +63 -0
- data/lib/grpc_server_reflection/descriptor_registry.rb +92 -0
- data/lib/grpc_server_reflection/service.rb +154 -0
- data/lib/grpc_server_reflection/version.rb +3 -0
- data/lib/grpc_server_reflection.rb +6 -0
- metadata +145 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
module GrpcServerReflection
|
|
2
|
+
class << self
|
|
3
|
+
# Manually set which services to reflect.
|
|
4
|
+
# If not set, auto-detects from the server's registered handlers.
|
|
5
|
+
#
|
|
6
|
+
# GrpcServerReflection.services = [MyService, OtherService]
|
|
7
|
+
# s.handle(GrpcServerReflection::Service)
|
|
8
|
+
#
|
|
9
|
+
attr_accessor :services
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Service < Grpc::Reflection::V1::ServerReflection::Service
|
|
13
|
+
def server_reflection_info(requests, call)
|
|
14
|
+
registry = self.class.registry_for(allowed_service_names(call))
|
|
15
|
+
|
|
16
|
+
Enumerator.new do |yielder|
|
|
17
|
+
requests.each do |request|
|
|
18
|
+
response = handle_request(request, registry)
|
|
19
|
+
yielder << response
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Class-level registry cache. Reset with `GrpcServerReflection::Service.reset_registry!`
|
|
25
|
+
class << self
|
|
26
|
+
def registry_for(allowed)
|
|
27
|
+
@mutex ||= Mutex.new
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
cache_key = allowed ? allowed.sort : nil
|
|
30
|
+
if @registry_cache_key != cache_key
|
|
31
|
+
@registry = nil
|
|
32
|
+
@registry_cache_key = cache_key
|
|
33
|
+
end
|
|
34
|
+
@registry ||= DescriptorRegistry.new(allowed_service_names: allowed)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def reset_registry!
|
|
39
|
+
@mutex ||= Mutex.new
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@registry = nil
|
|
42
|
+
@registry_cache_key = nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def allowed_service_names(call)
|
|
50
|
+
# Manual override takes priority
|
|
51
|
+
if GrpcServerReflection.services
|
|
52
|
+
return GrpcServerReflection.services.map(&:service_name).compact
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Auto-detect from server's registered RPCs
|
|
56
|
+
server = find_server(call)
|
|
57
|
+
return nil unless server
|
|
58
|
+
|
|
59
|
+
rpc_descs = server.instance_variable_get(:@rpc_descs)
|
|
60
|
+
return nil unless rpc_descs
|
|
61
|
+
|
|
62
|
+
rpc_descs.keys.map do |key|
|
|
63
|
+
parts = key.to_s.split('/')
|
|
64
|
+
parts[1] if parts.length >= 3
|
|
65
|
+
end.compact.uniq
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def find_server(call)
|
|
69
|
+
return nil unless call
|
|
70
|
+
|
|
71
|
+
# Try to find the server via ObjectSpace
|
|
72
|
+
# In a running gRPC server, there's typically one RpcServer instance
|
|
73
|
+
ObjectSpace.each_object(GRPC::RpcServer).first
|
|
74
|
+
rescue
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def handle_request(request, registry)
|
|
79
|
+
response = Grpc::Reflection::V1::ServerReflectionResponse.new(
|
|
80
|
+
valid_host: request.host,
|
|
81
|
+
original_request: request
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
case request.message_request
|
|
85
|
+
when :list_services
|
|
86
|
+
handle_list_services(response, registry)
|
|
87
|
+
when :file_by_filename
|
|
88
|
+
handle_file_by_filename(response, request.file_by_filename, registry)
|
|
89
|
+
when :file_containing_symbol
|
|
90
|
+
handle_file_containing_symbol(response, request.file_containing_symbol, registry)
|
|
91
|
+
when :file_containing_extension
|
|
92
|
+
handle_file_containing_extension(response, request.file_containing_extension, registry)
|
|
93
|
+
when :all_extension_numbers_of_type
|
|
94
|
+
handle_all_extension_numbers(response, request.all_extension_numbers_of_type, registry)
|
|
95
|
+
else
|
|
96
|
+
handle_error(response, GRPC::Core::StatusCodes::UNIMPLEMENTED, 'Request not implemented')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
response
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_list_services(response, registry)
|
|
103
|
+
services = registry.list_services.map do |name|
|
|
104
|
+
Grpc::Reflection::V1::ServiceResponse.new(name: name)
|
|
105
|
+
end
|
|
106
|
+
response.list_services_response = Grpc::Reflection::V1::ListServiceResponse.new(
|
|
107
|
+
service: services
|
|
108
|
+
)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def handle_file_by_filename(response, filename, registry)
|
|
112
|
+
descriptors = registry.file_descriptors_with_dependencies(filename)
|
|
113
|
+
if descriptors.empty?
|
|
114
|
+
handle_error(response, GRPC::Core::StatusCodes::NOT_FOUND, "File not found: #{filename}")
|
|
115
|
+
else
|
|
116
|
+
response.file_descriptor_response = Grpc::Reflection::V1::FileDescriptorResponse.new(
|
|
117
|
+
file_descriptor_proto: descriptors
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def handle_file_containing_symbol(response, symbol, registry)
|
|
123
|
+
filename = registry.find_filename_by_symbol(symbol)
|
|
124
|
+
if filename.nil?
|
|
125
|
+
handle_error(response, GRPC::Core::StatusCodes::NOT_FOUND, "Symbol not found: #{symbol}")
|
|
126
|
+
else
|
|
127
|
+
descriptors = registry.file_descriptors_with_dependencies(filename)
|
|
128
|
+
response.file_descriptor_response = Grpc::Reflection::V1::FileDescriptorResponse.new(
|
|
129
|
+
file_descriptor_proto: descriptors
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def handle_file_containing_extension(response, ext_request, registry)
|
|
135
|
+
handle_error(response, GRPC::Core::StatusCodes::NOT_FOUND,
|
|
136
|
+
"Extension not found for type: #{ext_request.containing_type}, number: #{ext_request.extension_number}")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def handle_all_extension_numbers(response, type_name, registry)
|
|
140
|
+
numbers = registry.find_extension_numbers(type_name)
|
|
141
|
+
response.all_extension_numbers_response = Grpc::Reflection::V1::ExtensionNumberResponse.new(
|
|
142
|
+
base_type_name: type_name,
|
|
143
|
+
extension_number: numbers
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def handle_error(response, code, message)
|
|
148
|
+
response.error_response = Grpc::Reflection::V1::ErrorResponse.new(
|
|
149
|
+
error_code: code,
|
|
150
|
+
error_message: message
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: grpc-server-reflection
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- zbokostya
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-04-04 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: grpc
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: google-protobuf
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '13.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '3.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: grpc-tools
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.0'
|
|
97
|
+
description: Implements the gRPC Server Reflection Protocol v1, enabling tools like
|
|
98
|
+
grpcurl and Postman to introspect gRPC services.
|
|
99
|
+
email:
|
|
100
|
+
executables: []
|
|
101
|
+
extensions: []
|
|
102
|
+
extra_rdoc_files: []
|
|
103
|
+
files:
|
|
104
|
+
- LICENSE
|
|
105
|
+
- README.md
|
|
106
|
+
- grpc/reflection/v1/reflection.proto
|
|
107
|
+
- grpc/reflection/v1/reflection_pb.rb
|
|
108
|
+
- grpc/reflection/v1/reflection_services_pb.rb
|
|
109
|
+
- grpc/reflection/v1alpha/reflection.proto
|
|
110
|
+
- grpc/reflection/v1alpha/reflection_pb.rb
|
|
111
|
+
- grpc/reflection/v1alpha/reflection_services_pb.rb
|
|
112
|
+
- lib/grpc_server_reflection.rb
|
|
113
|
+
- lib/grpc_server_reflection/descriptor_registry.rb
|
|
114
|
+
- lib/grpc_server_reflection/descriptor_registry/dependency_resolver.rb
|
|
115
|
+
- lib/grpc_server_reflection/descriptor_registry/file_descriptor_indexer.rb
|
|
116
|
+
- lib/grpc_server_reflection/descriptor_registry/object_space_indexer.rb
|
|
117
|
+
- lib/grpc_server_reflection/descriptor_registry/proto_builder.rb
|
|
118
|
+
- lib/grpc_server_reflection/descriptor_registry/type_mapping.rb
|
|
119
|
+
- lib/grpc_server_reflection/service.rb
|
|
120
|
+
- lib/grpc_server_reflection/version.rb
|
|
121
|
+
homepage: https://github.com/zbokostya/ruby-grpc-server-reflection
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
124
|
+
metadata: {}
|
|
125
|
+
post_install_message:
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
- "."
|
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">="
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '2.7'
|
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
140
|
+
requirements: []
|
|
141
|
+
rubygems_version: 3.1.6
|
|
142
|
+
signing_key:
|
|
143
|
+
specification_version: 4
|
|
144
|
+
summary: gRPC Server Reflection Protocol v1 for Ruby
|
|
145
|
+
test_files: []
|