-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rust: add demo Tonic server #4318
Changes from all commits
f3544ed
dacd2fe
155c40c
bb1f37a
864fd26
d743728
6a5ef83
984ad35
3ef1476
395bda0
50fa124
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
syntax = "proto3"; | ||
|
||
package demo; | ||
|
||
service Demo { | ||
rpc Add(AddRequest) returns (AddResponse); | ||
} | ||
|
||
message AddRequest { | ||
repeated int32 term = 1; | ||
} | ||
|
||
message AddResponse { | ||
int32 sum = 1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,4 +13,38 @@ See the License for the specific language governing permissions and | |
limitations under the License. | ||
==============================================================================*/ | ||
|
||
fn main() {} | ||
#![allow(clippy::needless_update)] // https://github.com/rust-lang/rust-clippy/issues/6323 | ||
|
||
use tonic::{transport::Server, Request, Response}; | ||
|
||
use rustboard_core::proto::demo as pb; | ||
|
||
#[derive(Debug, Default)] | ||
struct DemoHandler; | ||
|
||
#[tonic::async_trait] | ||
impl pb::demo_server::Demo for DemoHandler { | ||
async fn add( | ||
&self, | ||
request: Request<pb::AddRequest>, | ||
) -> Result<Response<pb::AddResponse>, tonic::Status> { | ||
let request = request.into_inner(); | ||
let sum: i32 = request.term.into_iter().sum(); | ||
let response = pb::AddResponse { | ||
sum, | ||
..Default::default() | ||
}; | ||
Ok(Response::new(response)) | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
let addr = "[::0]:6789".parse::<std::net::SocketAddr>()?; | ||
let handler = DemoHandler::default(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One part that is iffy in my head is, how does it know to invoke There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At a high level, the answer is: “the I’ve never read the generated code before, but we can take a look. It’s impl<T, B> Service<http::Request<B>> for DemoServer<T>
where
////////// Trait implemented for all handlers that implement the `Demo` trait,
////////// which includes our `DemoHandler` struct:
T: Demo,
////////// The request and error types need to be moveable between threads,
////////// shareable across threads, and with only static borrowed data; this
////////// way, the server has more opportunities to use threads for handling:
B: HttpBody + Send + Sync + 'static,
B::Error: Into<StdError> + Send + 'static,
{
// ...
fn call(&mut self, req: http::Request<B>) -> Self::Future {
let inner = self.inner.clone();
match req.uri().path() {
////////////////// Here's where we match against the gRPC request path...
"/demo.Demo/Add" => {
#[allow(non_camel_case_types)]
struct AddSvc<T: Demo>(pub Arc<T>);
impl<T: Demo> tonic::server::UnaryService<super::AddRequest> for AddSvc<T> {
type Response = super::AddResponse;
type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
fn call(
&mut self,
request: tonic::Request<super::AddRequest>,
) -> Self::Future {
let inner = self.0.clone();
////////////////////////////// ...and here's where we call our `async fn add`:
let fut = async move { (*inner).add(request).await };
Box::pin(fut)
}
}
let inner = self.inner.clone();
let fut = async move {
let interceptor = inner.1.clone();
let inner = inner.0;
let method = AddSvc(inner);
let codec = tonic::codec::ProstCodec::default();
let mut grpc = if let Some(interceptor) = interceptor {
tonic::server::Grpc::with_interceptor(codec, interceptor)
} else {
tonic::server::Grpc::new(codec)
};
let res = grpc.unary(method, req).await;
Ok(res)
};
Box::pin(fut)
}
////////////////// Here's the catch-all, with `Code.UNIMPLEMENTED == 12`:
_ => Box::pin(async move {
Ok(http::Response::builder()
.status(200)
.header("grpc-status", "12")
.body(tonic::body::BoxBody::empty())
.unwrap())
}),
}
}
} |
||
Server::builder() | ||
.add_service(pb::demo_server::DemoServer::new(handler)) | ||
.serve(addr) | ||
.await?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You didn’t comment on the postfix When I saw that Rust was considering implementing // JavaScript: prefix `await` syntax
const data = (await (await fetch("data/runs")).json()).field; // Rust: postfix `await` syntax
let data = fetch("data/runs").await?.json().await?.field; Once I saw this, I was immediately sold. With prefix const data = (await (await fetch("data/runs")).json()).field; /*
------------------ 1
----- 2
------ 3
----- 4
----- 5 */ and you have to deal with the associated parentheses: the first thing let data = fetch("data/runs").await?.json().await?.field; /*
--------------- 1 --- 2 --- 3 --- 4 --- 5 */ |
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah :) I knew something was weird about the generated file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah—I had
format(true)
(the default) originally, but that only workedon my machine because I have
rustfmt
installed; it failed on Travis.It would be nice for it to be formatted. We could format it pretty
easily if bazelbuild/rules_rust#388 were resolved, or we could copy what
bazelbuild/rules_rust#479 does and do it the hard way. I figured that to
start we could just accept the ugly generated code: we have the Rustdoc
server for normal API usage questions, and you can always run
rustfmt
on it locally if you want to look at the implementation.