use crate::proto::server_reflection_request::MessageRequest; use crate::proto::server_reflection_response::MessageResponse; pub use crate::proto::server_reflection_server::{ServerReflection, ServerReflectionServer}; use crate::proto::{ ExtensionNumberResponse, FileDescriptorResponse, ListServiceResponse, ServerReflectionRequest, ServerReflectionResponse, ServiceResponse, }; use prost::{DecodeError, Message}; use prost_types::{ DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, FileDescriptorSet, }; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::sync::Arc; use tokio::sync::mpsc; use tokio_stream::{wrappers::ReceiverStream, StreamExt}; use tonic::{Request, Response, Status, Streaming}; /// Represents an error in the construction of a gRPC Reflection Service. #[derive(Debug)] pub enum Error { /// An error was encountered decoding a `prost_types::FileDescriptorSet` from a buffer. DecodeError(prost::DecodeError), /// An invalid `prost_types::FileDescriptorProto` was encountered. InvalidFileDescriptorSet(String), } impl From for Error { fn from(e: DecodeError) -> Self { Error::DecodeError(e) } } impl std::error::Error for Error {} impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Error::DecodeError(_) => f.write_str("error decoding FileDescriptorSet from buffer"), Error::InvalidFileDescriptorSet(s) => { write!(f, "invalid FileDescriptorSet - {}", s) } } } } /// A builder used to construct a gRPC Reflection Service. #[derive(Debug)] pub struct Builder<'b> { file_descriptor_sets: Vec, encoded_file_descriptor_sets: Vec<&'b [u8]>, include_reflection_service: bool, service_names: Vec, use_all_service_names: bool, symbols: HashMap>, } impl<'b> Builder<'b> { /// Create a new builder that can configure a gRPC Reflection Service. pub fn configure() -> Self { Builder { file_descriptor_sets: Vec::new(), encoded_file_descriptor_sets: Vec::new(), include_reflection_service: true, service_names: Vec::new(), use_all_service_names: true, symbols: HashMap::new(), } } /// Registers an instance of `prost_types::FileDescriptorSet` with the gRPC Reflection /// Service builder. pub fn register_file_descriptor_set(mut self, file_descriptor_set: FileDescriptorSet) -> Self { self.file_descriptor_sets.push(file_descriptor_set); self } /// Registers a byte slice containing an encoded `prost_types::FileDescriptorSet` with /// the gRPC Reflection Service builder. pub fn register_encoded_file_descriptor_set( mut self, encoded_file_descriptor_set: &'b [u8], ) -> Self { self.encoded_file_descriptor_sets .push(encoded_file_descriptor_set); self } /// Serve the gRPC Reflection Service descriptor via the Reflection Service. This is enabled /// by default - set `include` to false to disable. pub fn include_reflection_service(mut self, include: bool) -> Self { self.include_reflection_service = include; self } /// Advertise a fully-qualified gRPC service name. /// /// If not called, then all services present in the registered file descriptor sets /// will be advertised. pub fn with_service_name(mut self, name: impl Into) -> Self { self.use_all_service_names = false; self.service_names.push(name.into()); self } /// Build a gRPC Reflection Service to be served via Tonic. pub fn build(mut self) -> Result, Error> { if self.include_reflection_service { self = self.register_encoded_file_descriptor_set(crate::proto::FILE_DESCRIPTOR_SET); } for encoded in &self.encoded_file_descriptor_sets { let decoded = FileDescriptorSet::decode(*encoded)?; self.file_descriptor_sets.push(decoded); } let all_fds = self.file_descriptor_sets.clone(); let mut files: HashMap> = HashMap::new(); for fds in all_fds { for fd in fds.file { let name = match fd.name.clone() { None => { return Err(Error::InvalidFileDescriptorSet("missing name".to_string())); } Some(n) => n, }; if files.contains_key(&name) { continue; } let fd = Arc::new(fd); files.insert(name, fd.clone()); self.process_file(fd)?; } } let service_names = self .service_names .iter() .map(|name| ServiceResponse { name: name.clone() }) .collect(); Ok(ServerReflectionServer::new(ReflectionService { state: Arc::new(ReflectionServiceState { service_names, files, symbols: self.symbols, }), })) } fn process_file(&mut self, fd: Arc) -> Result<(), Error> { let prefix = &fd.package.clone().unwrap_or_default(); for msg in &fd.message_type { self.process_message(fd.clone(), prefix, msg)?; } for en in &fd.enum_type { self.process_enum(fd.clone(), prefix, en)?; } for service in &fd.service { let service_name = extract_name(prefix, "service", service.name.as_ref())?; if self.use_all_service_names { self.service_names.push(service_name.clone()); } self.symbols.insert(service_name.clone(), fd.clone()); for method in &service.method { let method_name = extract_name(&service_name, "method", method.name.as_ref())?; self.symbols.insert(method_name, fd.clone()); } } Ok(()) } fn process_message( &mut self, fd: Arc, prefix: &str, msg: &DescriptorProto, ) -> Result<(), Error> { let message_name = extract_name(prefix, "message", msg.name.as_ref())?; self.symbols.insert(message_name.clone(), fd.clone()); for nested in &msg.nested_type { self.process_message(fd.clone(), &message_name, nested)?; } for en in &msg.enum_type { self.process_enum(fd.clone(), &message_name, en)?; } for field in &msg.field { self.process_field(fd.clone(), &message_name, field)?; } for oneof in &msg.oneof_decl { let oneof_name = extract_name(&message_name, "oneof", oneof.name.as_ref())?; self.symbols.insert(oneof_name, fd.clone()); } Ok(()) } fn process_enum( &mut self, fd: Arc, prefix: &str, en: &EnumDescriptorProto, ) -> Result<(), Error> { let enum_name = extract_name(prefix, "enum", en.name.as_ref())?; self.symbols.insert(enum_name.clone(), fd.clone()); for value in &en.value { let value_name = extract_name(&enum_name, "enum value", value.name.as_ref())?; self.symbols.insert(value_name, fd.clone()); } Ok(()) } fn process_field( &mut self, fd: Arc, prefix: &str, field: &FieldDescriptorProto, ) -> Result<(), Error> { let field_name = extract_name(prefix, "field", field.name.as_ref())?; self.symbols.insert(field_name, fd); Ok(()) } } fn extract_name( prefix: &str, name_type: &str, maybe_name: Option<&String>, ) -> Result { match maybe_name { None => Err(Error::InvalidFileDescriptorSet(format!( "missing {} name", name_type ))), Some(name) => { if prefix.is_empty() { Ok(name.to_string()) } else { Ok(format!("{}.{}", prefix, name)) } } } } #[derive(Debug)] struct ReflectionServiceState { service_names: Vec, files: HashMap>, symbols: HashMap>, } impl ReflectionServiceState { fn list_services(&self) -> MessageResponse { MessageResponse::ListServicesResponse(ListServiceResponse { service: self.service_names.clone(), }) } fn symbol_by_name(&self, symbol: &str) -> Result { match self.symbols.get(symbol) { None => Err(Status::not_found(format!("symbol '{}' not found", symbol))), Some(fd) => { let mut encoded_fd = Vec::new(); if fd.clone().encode(&mut encoded_fd).is_err() { return Err(Status::internal("encoding error")); }; Ok(MessageResponse::FileDescriptorResponse( FileDescriptorResponse { file_descriptor_proto: vec![encoded_fd], }, )) } } } fn file_by_filename(&self, filename: &str) -> Result { match self.files.get(filename) { None => Err(Status::not_found(format!("file '{}' not found", filename))), Some(fd) => { let mut encoded_fd = Vec::new(); if fd.clone().encode(&mut encoded_fd).is_err() { return Err(Status::internal("encoding error")); } Ok(MessageResponse::FileDescriptorResponse( FileDescriptorResponse { file_descriptor_proto: vec![encoded_fd], }, )) } } } } #[derive(Debug)] struct ReflectionService { state: Arc, } #[tonic::async_trait] impl ServerReflection for ReflectionService { type ServerReflectionInfoStream = ReceiverStream>; async fn server_reflection_info( &self, req: Request>, ) -> Result, Status> { let mut req_rx = req.into_inner(); let (resp_tx, resp_rx) = mpsc::channel::>(1); let state = self.state.clone(); tokio::spawn(async move { while let Some(req) = req_rx.next().await { let req = match req { Ok(req) => req, Err(_) => { return; } }; let resp_msg = match req.message_request.clone() { None => Err(Status::invalid_argument("invalid MessageRequest")), Some(msg) => match msg { MessageRequest::FileByFilename(s) => state.file_by_filename(&s), MessageRequest::FileContainingSymbol(s) => state.symbol_by_name(&s), MessageRequest::FileContainingExtension(_) => { Err(Status::not_found("extensions are not supported")) } MessageRequest::AllExtensionNumbersOfType(_) => { // NOTE: Workaround. Some grpc clients (e.g. grpcurl) expect this method not to fail. // https://github.com/hyperium/tonic/issues/1077 Ok(MessageResponse::AllExtensionNumbersResponse( ExtensionNumberResponse::default(), )) } MessageRequest::ListServices(_) => Ok(state.list_services()), }, }; match resp_msg { Ok(resp_msg) => { let resp = ServerReflectionResponse { valid_host: req.host.clone(), original_request: Some(req.clone()), message_response: Some(resp_msg), }; resp_tx.send(Ok(resp)).await.expect("send"); } Err(status) => { resp_tx.send(Err(status)).await.expect("send"); return; } } } }); Ok(Response::new(ReceiverStream::new(resp_rx))) } }