Skip to content

Commit

Permalink
feat(reflection): Add server reflection impl
Browse files Browse the repository at this point in the history
Co-authored-by: Samani G. Gikandi <[email protected]>
  • Loading branch information
jen20 and sgg committed Jan 23, 2021
1 parent fd317ca commit ac8321c
Show file tree
Hide file tree
Showing 8 changed files with 628 additions and 1 deletion.
6 changes: 6 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,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 @@ -173,6 +177,8 @@ http-body = "0.4"
pin-project = "1.0"
# Health example
tonic-health = { path = "../tonic-health" }
# Reflection example
tonic-reflection = { path = "../tonic-reflection" }
listenfd = "0.3"

[build-dependencies]
Expand Down
10 changes: 9 additions & 1 deletion examples/build.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
use std::env;
use std::path::PathBuf;

fn main() {
tonic_build::configure()
.type_attribute("routeguide.Point", "#[derive(Hash)]")
.compile(&["proto/routeguide/route_guide.proto"], &["proto"])
.unwrap();

tonic_build::compile_protos("proto/helloworld/helloworld.proto").unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
tonic_build::configure()
.file_descriptor_set_path(out_dir.join("helloworld_descriptor.bin"))
.compile(&["proto/helloworld/helloworld.proto"], &["proto"])
.unwrap();

tonic_build::compile_protos("proto/echo/echo.proto").unwrap();

tonic_build::configure()
Expand Down
46 changes: 46 additions & 0 deletions examples/src/reflection/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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!("helloworld_descriptor");
}

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

[dependencies]
bytes = "1.0"
prost = "0.7"
prost-types = "0.7"
tokio = { version = "1.0", features = ["sync"] }
tokio-stream = "0.1"
tonic = { version = "0.4", path = "../tonic", features = ["codegen", "prost"] }

[build-dependencies]
tonic-build = { version = "0.4", path = "../tonic-build" }
16 changes: 16 additions & 0 deletions tonic-reflection/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::env;
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let reflection_descriptor =
PathBuf::from(env::var("OUT_DIR").unwrap()).join("reflection_v1alpha1.bin");

tonic_build::configure()
.file_descriptor_set_path(&reflection_descriptor)
.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;
}
26 changes: 26 additions & 0 deletions tonic-reflection/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! A `tonic` based gRPC Server Reflection implementation.

#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![doc(
html_logo_url = "https://github.com/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://github.com/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!("reflection_v1alpha1");
}

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

0 comments on commit ac8321c

Please sign in to comment.