Skip to content

Commit

Permalink
feat(reflection): Add server reflection impl
Browse files Browse the repository at this point in the history
  • Loading branch information
jen20 committed May 28, 2020
1 parent 5d2fd8a commit 83b2454
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ members = [
[replace]
'prost:0.6.1' = { git = "https:/jen20/prost", branch = "jen20/file-descriptor-set" }
'prost-build:0.6.1' = { git = "https:/jen20/prost", branch = "jen20/file-descriptor-set" }
'prost-types:0.6.1' = { git = "https:/jen20/prost", branch = "jen20/file-descriptor-set" }
6 changes: 6 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ path = "src/hyper_warp/server.rs"
name = "health-server"
path = "src/health/server.rs"

[[bin]]
name = "reflection-server"
path = "src/reflection/server.rs"

[[bin]]
name = "autoreload-server"
path = "src/autoreload/server.rs"
Expand Down Expand Up @@ -144,6 +148,8 @@ http-body = "0.3"
pin-project = "0.4"
# Health example
tonic-health = { path = "../tonic-health" }
# Reflection example
tonic-reflection = { path = "../tonic-reflection" }
listenfd = "0.3"

[build-dependencies]
Expand Down
5 changes: 4 additions & 1 deletion examples/build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
fn main() {
tonic_build::compile_protos("proto/helloworld/helloworld.proto").unwrap();
tonic_build::configure()
.include_file_descriptor_set(true)
.compile(&["proto/helloworld/helloworld.proto"], &["proto/"])
.unwrap();
tonic_build::compile_protos("proto/routeguide/route_guide.proto").unwrap();
tonic_build::compile_protos("proto/echo/echo.proto").unwrap();
tonic_build::compile_protos("proto/google/pubsub/pubsub.proto").unwrap();
Expand Down
45 changes: 45 additions & 0 deletions examples/src/reflection/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use tonic::transport::Server;
use tonic::{Request, Response, Status};

mod proto {
tonic::include_proto!("helloworld");

pub(crate) const FILE_DESCRIPTOR_SET: &'static [u8] = tonic::include_file_descriptor_set!();
}

#[derive(Default)]
pub struct MyGreeter {}

#[tonic::async_trait]
impl proto::greeter_server::Greeter for MyGreeter {
async fn say_hello(
&self,
request: Request<proto::HelloRequest>,
) -> Result<Response<proto::HelloReply>, Status> {
println!("Got a request from {:?}", request.remote_addr());

let reply = proto::HelloReply {
message: format!("Hello {}!", request.into_inner().name),
};
Ok(Response::new(reply))
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(proto::FILE_DESCRIPTOR_SET)
.build()
.unwrap();

let addr = "[::1]:50052".parse().unwrap();
let greeter = MyGreeter::default();

Server::builder()
.add_service(service)
.add_service(proto::greeter_server::GreeterServer::new(greeter))
.serve(addr)
.await?;

Ok(())
}
24 changes: 24 additions & 0 deletions tonic-reflection/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "tonic-reflection"
version = "0.1.0"
authors = ["James Nugent <[email protected]>"]
edition = "2018"
license = "MIT"
repository = "https:/hyperium/tonic"
homepage = "https:/hyperium/tonic"
description = """
Server Reflection module of `tonic` gRPC implementation.
"""
readme = "README.md"
categories = ["network-programming", "asynchronous"]
keywords = ["rpc", "grpc", "async", "reflection"]

[dependencies]
bytes = "0.5"
prost = "0.6"
prost-types = "0.6"
tokio = { version = "0.2", features = ["sync", "stream"] }
tonic = { version = "0.2", path = "../tonic", features = ["codegen", "prost"] }

[build-dependencies]
tonic-build = { version = "0.2", path = "../tonic-build" }
10 changes: 10 additions & 0 deletions tonic-reflection/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.include_file_descriptor_set(true)
.build_server(true)
.build_client(false)
.format(true)
.compile(&["proto/reflection.proto"], &["proto/"])?;

Ok(())
}
136 changes: 136 additions & 0 deletions tonic-reflection/proto/reflection.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2016 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Service exported by server reflection

syntax = "proto3";

package grpc.reflection.v1alpha;

service ServerReflection {
// The reflection service is structured as a bidirectional stream, ensuring
// all related requests go to a single server.
rpc ServerReflectionInfo(stream ServerReflectionRequest)
returns (stream ServerReflectionResponse);
}

// The message sent by the client when calling ServerReflectionInfo method.
message ServerReflectionRequest {
string host = 1;
// To use reflection service, the client should set one of the following
// fields in message_request. The server distinguishes requests by their
// defined field and then handles them using corresponding methods.
oneof message_request {
// Find a proto file by the file name.
string file_by_filename = 3;

// Find the proto file that declares the given fully-qualified symbol name.
// This field should be a fully-qualified symbol name
// (e.g. <package>.<service>[.<method>] or <package>.<type>).
string file_containing_symbol = 4;

// Find the proto file which defines an extension extending the given
// message type with the given field number.
ExtensionRequest file_containing_extension = 5;

// Finds the tag numbers used by all known extensions of extendee_type, and
// appends them to ExtensionNumberResponse in an undefined order.
// Its corresponding method is best-effort: it's not guaranteed that the
// reflection service will implement this method, and it's not guaranteed
// that this method will provide all extensions. Returns
// StatusCode::UNIMPLEMENTED if it's not implemented.
// This field should be a fully-qualified type name. The format is
// <package>.<type>
string all_extension_numbers_of_type = 6;

// List the full names of registered services. The content will not be
// checked.
string list_services = 7;
}
}

// The type name and extension number sent by the client when requesting
// file_containing_extension.
message ExtensionRequest {
// Fully-qualified type name. The format should be <package>.<type>
string containing_type = 1;
int32 extension_number = 2;
}

// The message sent by the server to answer ServerReflectionInfo method.
message ServerReflectionResponse {
string valid_host = 1;
ServerReflectionRequest original_request = 2;
// The server sets one of the following fields according to the
// message_request in the request.
oneof message_response {
// This message is used to answer file_by_filename, file_containing_symbol,
// file_containing_extension requests with transitive dependencies.
// As the repeated label is not allowed in oneof fields, we use a
// FileDescriptorResponse message to encapsulate the repeated fields.
// The reflection service is allowed to avoid sending FileDescriptorProtos
// that were previously sent in response to earlier requests in the stream.
FileDescriptorResponse file_descriptor_response = 4;

// This message is used to answer all_extension_numbers_of_type requests.
ExtensionNumberResponse all_extension_numbers_response = 5;

// This message is used to answer list_services requests.
ListServiceResponse list_services_response = 6;

// This message is used when an error occurs.
ErrorResponse error_response = 7;
}
}

// Serialized FileDescriptorProto messages sent by the server answering
// a file_by_filename, file_containing_symbol, or file_containing_extension
// request.
message FileDescriptorResponse {
// Serialized FileDescriptorProto messages. We avoid taking a dependency on
// descriptor.proto, which uses proto2 only features, by making them opaque
// bytes instead.
repeated bytes file_descriptor_proto = 1;
}

// A list of extension numbers sent by the server answering
// all_extension_numbers_of_type request.
message ExtensionNumberResponse {
// Full name of the base type, including the package name. The format
// is <package>.<type>
string base_type_name = 1;
repeated int32 extension_number = 2;
}

// A list of ServiceResponse sent by the server answering list_services request.
message ListServiceResponse {
// The information of each service may be expanded in the future, so we use
// ServiceResponse message to encapsulate it.
repeated ServiceResponse service = 1;
}

// The information of a single service used by ListServiceResponse to answer
// list_services request.
message ServiceResponse {
// Full name of a registered service, including its package name. The format
// is <package>.<service>
string name = 1;
}

// The error code and error message sent by the server when an error occurs.
message ErrorResponse {
// This field uses the error codes defined in grpc::StatusCode.
int32 error_code = 1;
string error_message = 2;
}
25 changes: 25 additions & 0 deletions tonic-reflection/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! A `tonic` based gRPC Server Reflection implementation.

#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![doc(
html_logo_url = "https:/hyperium/tonic/raw/master/.github/assets/tonic-docs.png"
)]
#![doc(html_root_url = "https://docs.rs/tonic-reflection/0.1.0")]
#![doc(issue_tracker_base_url = "https:/hyperium/tonic/issues/")]
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
#![cfg_attr(docsrs, feature(doc_cfg))]

mod proto {
#![allow(unreachable_pub)]
tonic::include_proto!("grpc.reflection.v1alpha");

pub(crate) const FILE_DESCRIPTOR_SET: &'static [u8] = tonic::include_file_descriptor_set!();
}

/// Implementation of the server component of gRPC Server Reflection.
pub mod server;
Loading

0 comments on commit 83b2454

Please sign in to comment.