diff --git a/Rust/rust-grpc-helloworld/.gitignore b/Rust/rust-grpc-helloworld/.gitignore new file mode 100644 index 0000000..0188116 --- /dev/null +++ b/Rust/rust-grpc-helloworld/.gitignore @@ -0,0 +1,4 @@ +### Rust + +Cargo.lock +/target/ \ No newline at end of file diff --git a/Rust/rust-grpc-helloworld/Cargo.toml b/Rust/rust-grpc-helloworld/Cargo.toml new file mode 100644 index 0000000..c17bca2 --- /dev/null +++ b/Rust/rust-grpc-helloworld/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rust-grpc-helloworld" +version = "0.1.0" +authors = ["GitStonic "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +simple_logger = "1.3.0" +protobuf = "2.8.1" +futures = "0.1.29" +grpcio = "0.4" +log = "0.4" diff --git a/Rust/rust-grpc-helloworld/README.md b/Rust/rust-grpc-helloworld/README.md new file mode 100644 index 0000000..e8e8493 --- /dev/null +++ b/Rust/rust-grpc-helloworld/README.md @@ -0,0 +1,3 @@ +# rust-grpc-hello-world + +An Rust gRPC hello world example... diff --git a/Rust/rust-grpc-helloworld/protos/HelloWorld.proto b/Rust/rust-grpc-helloworld/protos/HelloWorld.proto new file mode 100644 index 0000000..c2de538 --- /dev/null +++ b/Rust/rust-grpc-helloworld/protos/HelloWorld.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package HelloWorld; + +service HelloWorld { + rpc SendHello (Hello) returns (World) {} +} + +message World { + string content = 1; +} + +message Hello { + string content = 1; +} + diff --git a/Rust/rust-grpc-helloworld/src/client/mod.rs b/Rust/rust-grpc-helloworld/src/client/mod.rs new file mode 100644 index 0000000..6bbe598 --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/client/mod.rs @@ -0,0 +1,23 @@ +use super::proto::{HelloWorld as hello_world, HelloWorld_grpc as hello_world_grpc}; + +use std::io::{self, Read}; +use std::sync::Arc; + +use grpcio::{ChannelBuilder, EnvBuilder}; +use hello_world_grpc::HelloWorldClient; +use hello_world::Hello; + +pub fn start() { + let env = Arc::new(EnvBuilder::new().build()); + let ch = ChannelBuilder::new(env).connect("127.0.0.1:50051"); + let client = HelloWorldClient::new(ch); + + info!("Client connected to 127.0.0.1:50051"); + + let mut req = Hello::default(); + req.set_content("World".to_owned()); + let reply = client.send_hello(&req) + .expect("Cannot send method"); + + info!("From server recieved: {}...", reply.get_content()); +} \ No newline at end of file diff --git a/Rust/rust-grpc-helloworld/src/main.rs b/Rust/rust-grpc-helloworld/src/main.rs new file mode 100644 index 0000000..0e936fe --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/main.rs @@ -0,0 +1,23 @@ +extern crate simple_logger; +extern crate futures; +extern crate grpcio; +#[macro_use] +extern crate log; + +use std::thread; + +mod server; +mod client; +mod proto; + +fn main() { + simple_logger::init(); + + info!("Initializing server..."); + + thread::spawn(move || server::start()); + + info!("Starting client process..."); + + client::start(); +} diff --git a/Rust/rust-grpc-helloworld/src/proto/HelloWorld.rs b/Rust/rust-grpc-helloworld/src/proto/HelloWorld.rs new file mode 100644 index 0000000..9db2428 --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/proto/HelloWorld.rs @@ -0,0 +1,403 @@ +// This file is generated by rust-protobuf 2.8.1. Do not edit +// @generated + +// https://github.com/Manishearth/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] +//! Generated file from `protos/HelloWorld.proto` + +use protobuf::Message as Message_imported_for_functions; +use protobuf::ProtobufEnum as ProtobufEnum_imported_for_functions; + +/// Generated files are compatible only with the same version +/// of protobuf runtime. +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_8_1; + +#[derive(PartialEq,Clone,Default)] +pub struct World { + // message fields + pub content: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a World { + fn default() -> &'a World { + ::default_instance() + } +} + +impl World { + pub fn new() -> World { + ::std::default::Default::default() + } + + // string content = 1; + + + pub fn get_content(&self) -> &str { + &self.content + } + pub fn clear_content(&mut self) { + self.content.clear(); + } + + // Param is passed by value, moved + pub fn set_content(&mut self, v: ::std::string::String) { + self.content = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_content(&mut self) -> &mut ::std::string::String { + &mut self.content + } + + // Take field + pub fn take_content(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.content, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for World { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.content.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.content); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.content.is_empty() { + os.write_string(1, &self.content)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> World { + World::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "content", + |m: &World| { &m.content }, + |m: &mut World| { &mut m.content }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "World", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static World { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const World, + }; + unsafe { + instance.get(World::new) + } + } +} + +impl ::protobuf::Clear for World { + fn clear(&mut self) { + self.content.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for World { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for World { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] +pub struct Hello { + // message fields + pub content: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a Hello { + fn default() -> &'a Hello { + ::default_instance() + } +} + +impl Hello { + pub fn new() -> Hello { + ::std::default::Default::default() + } + + // string content = 1; + + + pub fn get_content(&self) -> &str { + &self.content + } + pub fn clear_content(&mut self) { + self.content.clear(); + } + + // Param is passed by value, moved + pub fn set_content(&mut self, v: ::std::string::String) { + self.content = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_content(&mut self) -> &mut ::std::string::String { + &mut self.content + } + + // Take field + pub fn take_content(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.content, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for Hello { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.content.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.content); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.content.is_empty() { + os.write_string(1, &self.content)?; + } + os.write_unknown_fields(self.get_unknown_fields())?; + ::std::result::Result::Ok(()) + } + + fn get_cached_size(&self) -> u32 { + self.cached_size.get() + } + + fn get_unknown_fields(&self) -> &::protobuf::UnknownFields { + &self.unknown_fields + } + + fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields { + &mut self.unknown_fields + } + + fn as_any(&self) -> &dyn (::std::any::Any) { + self as &dyn (::std::any::Any) + } + fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) { + self as &mut dyn (::std::any::Any) + } + fn into_any(self: Box) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> Hello { + Hello::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static mut descriptor: ::protobuf::lazy::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::reflect::MessageDescriptor, + }; + unsafe { + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "content", + |m: &Hello| { &m.content }, + |m: &mut Hello| { &mut m.content }, + )); + ::protobuf::reflect::MessageDescriptor::new::( + "Hello", + fields, + file_descriptor_proto() + ) + }) + } + } + + fn default_instance() -> &'static Hello { + static mut instance: ::protobuf::lazy::Lazy = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const Hello, + }; + unsafe { + instance.get(Hello::new) + } + } +} + +impl ::protobuf::Clear for Hello { + fn clear(&mut self) { + self.content.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for Hello { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for Hello { + fn as_ref(&self) -> ::protobuf::reflect::ProtobufValueRef { + ::protobuf::reflect::ProtobufValueRef::Message(self) + } +} + +static file_descriptor_proto_data: &'static [u8] = b"\ + \n\x17protos/HelloWorld.proto\x12\nHelloWorld\"!\n\x05World\x12\x18\n\ + \x07content\x18\x01\x20\x01(\tR\x07content\"!\n\x05Hello\x12\x18\n\x07co\ + ntent\x18\x01\x20\x01(\tR\x07content2A\n\nHelloWorld\x123\n\tSendHello\ + \x12\x11.HelloWorld.Hello\x1a\x11.HelloWorld.World\"\0J\xa7\x02\n\x06\ + \x12\x04\0\0\x0e\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\x08\n\x01\x02\x12\ + \x03\x02\x08\x12\n\n\n\x02\x06\0\x12\x04\x04\0\x06\x01\n\n\n\x03\x06\0\ + \x01\x12\x03\x04\x08\x12\n\x0b\n\x04\x06\0\x02\0\x12\x03\x05\x03+\n\x0c\ + \n\x05\x06\0\x02\0\x01\x12\x03\x05\x07\x10\n\x0c\n\x05\x06\0\x02\0\x02\ + \x12\x03\x05\x12\x17\n\x0c\n\x05\x06\0\x02\0\x03\x12\x03\x05\"'\n\n\n\ + \x02\x04\0\x12\x04\x08\0\n\x01\n\n\n\x03\x04\0\x01\x12\x03\x08\x08\r\n\ + \x0b\n\x04\x04\0\x02\0\x12\x03\t\x02\x15\n\r\n\x05\x04\0\x02\0\x04\x12\ + \x04\t\x02\x08\x0f\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\t\x02\x08\n\x0c\n\ + \x05\x04\0\x02\0\x01\x12\x03\t\t\x10\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\ + \t\x13\x14\n\n\n\x02\x04\x01\x12\x04\x0c\0\x0e\x01\n\n\n\x03\x04\x01\x01\ + \x12\x03\x0c\x08\r\n\x0b\n\x04\x04\x01\x02\0\x12\x03\r\x02\x15\n\r\n\x05\ + \x04\x01\x02\0\x04\x12\x04\r\x02\x0c\x0f\n\x0c\n\x05\x04\x01\x02\0\x05\ + \x12\x03\r\x02\x08\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\r\t\x10\n\x0c\n\ + \x05\x04\x01\x02\0\x03\x12\x03\r\x13\x14b\x06proto3\ +"; + +static mut file_descriptor_proto_lazy: ::protobuf::lazy::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::lazy::Lazy { + lock: ::protobuf::lazy::ONCE_INIT, + ptr: 0 as *const ::protobuf::descriptor::FileDescriptorProto, +}; + +fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto { + ::protobuf::parse_from_bytes(file_descriptor_proto_data).unwrap() +} + +pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { + unsafe { + file_descriptor_proto_lazy.get(|| { + parse_descriptor_proto() + }) + } +} diff --git a/Rust/rust-grpc-helloworld/src/proto/HelloWorld_grpc.rs b/Rust/rust-grpc-helloworld/src/proto/HelloWorld_grpc.rs new file mode 100644 index 0000000..6514153 --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/proto/HelloWorld_grpc.rs @@ -0,0 +1,71 @@ +// This file is generated. Do not edit +// @generated + +// https://github.com/Manishearth/rust-clippy/issues/702 +#![allow(unknown_lints)] +#![allow(clippy::all)] + +#![cfg_attr(rustfmt, rustfmt_skip)] + +#![allow(box_pointers)] +#![allow(dead_code)] +#![allow(missing_docs)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(trivial_casts)] +#![allow(unsafe_code)] +#![allow(unused_imports)] +#![allow(unused_results)] + +const METHOD_HELLO_WORLD_SEND_HELLO: ::grpcio::Method = ::grpcio::Method { + ty: ::grpcio::MethodType::Unary, + name: "/HelloWorld.HelloWorld/SendHello", + req_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, + resp_mar: ::grpcio::Marshaller { ser: ::grpcio::pb_ser, de: ::grpcio::pb_de }, +}; + +#[derive(Clone)] +pub struct HelloWorldClient { + client: ::grpcio::Client, +} + +impl HelloWorldClient { + pub fn new(channel: ::grpcio::Channel) -> Self { + HelloWorldClient { + client: ::grpcio::Client::new(channel), + } + } + + pub fn send_hello_opt(&self, req: &super::HelloWorld::Hello, opt: ::grpcio::CallOption) -> ::grpcio::Result { + self.client.unary_call(&METHOD_HELLO_WORLD_SEND_HELLO, req, opt) + } + + pub fn send_hello(&self, req: &super::HelloWorld::Hello) -> ::grpcio::Result { + self.send_hello_opt(req, ::grpcio::CallOption::default()) + } + + pub fn send_hello_async_opt(&self, req: &super::HelloWorld::Hello, opt: ::grpcio::CallOption) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver> { + self.client.unary_call_async(&METHOD_HELLO_WORLD_SEND_HELLO, req, opt) + } + + pub fn send_hello_async(&self, req: &super::HelloWorld::Hello) -> ::grpcio::Result<::grpcio::ClientUnaryReceiver> { + self.send_hello_async_opt(req, ::grpcio::CallOption::default()) + } + pub fn spawn(&self, f: F) where F: ::futures::Future + Send + 'static { + self.client.spawn(f) + } +} + +pub trait HelloWorld { + fn send_hello(&mut self, ctx: ::grpcio::RpcContext, req: super::HelloWorld::Hello, sink: ::grpcio::UnarySink); +} + +pub fn create_hello_world(s: S) -> ::grpcio::Service { + let mut builder = ::grpcio::ServiceBuilder::new(); + let mut instance = s.clone(); + builder = builder.add_unary_handler(&METHOD_HELLO_WORLD_SEND_HELLO, move |ctx, req, resp| { + instance.send_hello(ctx, req, resp) + }); + builder.build() +} diff --git a/Rust/rust-grpc-helloworld/src/proto/mod.rs b/Rust/rust-grpc-helloworld/src/proto/mod.rs new file mode 100644 index 0000000..b71c6f6 --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/proto/mod.rs @@ -0,0 +1,2 @@ +pub mod HelloWorld; +pub mod HelloWorld_grpc; diff --git a/Rust/rust-grpc-helloworld/src/server/mod.rs b/Rust/rust-grpc-helloworld/src/server/mod.rs new file mode 100644 index 0000000..8fbef9e --- /dev/null +++ b/Rust/rust-grpc-helloworld/src/server/mod.rs @@ -0,0 +1,57 @@ +use super::proto::{HelloWorld as hello_world, HelloWorld_grpc as hello_world_grpc}; + +use std::thread; +use std::io::{self, Read}; +use std::sync::Arc; + +use futures::sync::oneshot; +use futures::Future; + +use grpcio::{ChannelBuilder, Environment, ResourceQuota, RpcContext, ServerBuilder, UnarySink}; +use hello_world_grpc::{create_hello_world, HelloWorld}; +use hello_world::{Hello, World}; + +#[derive(Debug, Clone)] +pub struct HelloWorldService; + +impl HelloWorld for HelloWorldService { + fn send_hello (&mut self, ctx: RpcContext<'_>, req: Hello, sink: UnarySink) { + let msg = format!("Hello, {}", req.get_content()); + let mut resp = World::default(); + resp.set_content(msg); + + let f = sink + .success(resp) + .map_err(move |e| error!("failed to send content, {:?}: {:?}", req, e)); + + ctx.spawn(f); + } +} + +pub fn start () { + let env = Arc::new(Environment::new(1)); + let service = create_hello_world(HelloWorldService); + + let mut server = ServerBuilder::new(env) + .register_service(service) + .bind("127.0.0.1", 50_051) + .build() + .unwrap(); + + server.start(); + + for &(ref host, port) in server.bind_addrs() { + info!("listening on {}:{}", host, port); + } + + let (tx, rx) = oneshot::channel(); + + thread::spawn(move || { + info!("Press ENTER to exit..."); + let _ = io::stdin().read(&mut [0]).unwrap(); + tx.send(()) + }); + + let _ = rx.wait(); + let _ = server.shutdown().wait(); +}