blob: c7bcd153100819f8e905830c1057be5343c196b7 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
use std::{io::ErrorKind, pin::Pin};
use async_trait::async_trait;
use dubbo::filter::{context::ContextFilter, timeout::TimeoutFilter};
use futures_util::{Stream, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use dubbo::codegen::*;
use example_echo::generated::generated::{
echo_server::{register_server, Echo, EchoServer},
EchoRequest, EchoResponse,
};
type ResponseStream =
Pin<Box<dyn Stream<Item = Result<EchoResponse, dubbo::status::Status>> + Send>>;
#[derive(Clone)]
pub struct FakeFilter {}
impl Filter for FakeFilter {
fn call(&mut self, req: Request<()>) -> Result<Request<()>, dubbo::status::Status> {
println!("server fake filter: {:?}", req.metadata);
Ok(req)
}
}
#[tokio::main]
async fn main() {
dubbo_logger::init();
register_server(EchoServerImpl {
name: "echo".to_string(),
});
let server = EchoServerImpl::default();
let s = EchoServer::<EchoServerImpl>::with_filter(server, FakeFilter {});
let timeout_filter = FilterService::new(s, TimeoutFilter {});
let context_filter = FilterService::new(timeout_filter, ContextFilter {});
dubbo::protocol::triple::TRIPLE_SERVICES
.write()
.unwrap()
.insert(
"grpc.examples.echo.Echo".to_string(),
dubbo::utils::boxed_clone::BoxCloneService::new(context_filter),
);
let builder = ServerBuilder::new()
.with_listener("tcp".to_string())
.with_tls("fixtures/server.crt", "fixtures/server.key")
.with_service_names(vec!["grpc.examples.echo.Echo".to_string()])
.with_addr("127.0.0.1:8889");
builder.build().serve().await.unwrap();
}
#[allow(dead_code)]
#[derive(Default, Clone)]
struct EchoServerImpl {
name: String,
}
// #[async_trait]
#[async_trait]
impl Echo for EchoServerImpl {
async fn unary_echo(
&self,
req: Request<EchoRequest>,
) -> Result<Response<EchoResponse>, dubbo::status::Status> {
println!("EchoServer::hello {:?}", req.metadata);
Ok(Response::new(EchoResponse {
message: "hello, dubbo-rust".to_string(),
}))
}
type ServerStreamingEchoStream = ResponseStream;
async fn server_streaming_echo(
&self,
req: Request<EchoRequest>,
) -> Result<Response<Self::ServerStreamingEchoStream>, dubbo::status::Status> {
println!("server_streaming_echo: {:?}", req.into_inner());
let data = vec![
Result::<_, dubbo::status::Status>::Ok(EchoResponse {
message: "msg1 from tls-server".to_string(),
}),
Result::<_, dubbo::status::Status>::Ok(EchoResponse {
message: "msg2 from tls-server".to_string(),
}),
Result::<_, dubbo::status::Status>::Ok(EchoResponse {
message: "msg3 from tls-server".to_string(),
}),
];
let resp = futures_util::stream::iter(data);
Ok(Response::new(Box::pin(resp)))
}
async fn client_streaming_echo(
&self,
req: Request<Decoding<EchoRequest>>,
) -> Result<Response<EchoResponse>, dubbo::status::Status> {
let mut s = req.into_inner();
loop {
let result = s.next().await;
match result {
Some(Ok(val)) => println!("result: {:?}", val),
Some(Err(val)) => println!("err: {:?}", val),
None => break,
}
}
Ok(Response::new(EchoResponse {
message: "hello tls-client streaming".to_string(),
}))
}
type BidirectionalStreamingEchoStream = ResponseStream;
async fn bidirectional_streaming_echo(
&self,
request: Request<Decoding<EchoRequest>>,
) -> Result<Response<Self::BidirectionalStreamingEchoStream>, dubbo::status::Status> {
println!(
"EchoServer::bidirectional_streaming_echo, grpc header: {:?}",
request.metadata
);
let mut in_stream = request.into_inner();
let (tx, rx) = mpsc::channel(128);
// this spawn here is required if you want to handle connection error.
// If we just map `in_stream` and write it back as `out_stream` the `out_stream`
// will be drooped when connection error occurs and error will never be propagated
// to mapped version of `in_stream`.
tokio::spawn(async move {
while let Some(result) = in_stream.next().await {
match result {
Ok(v) => {
// if v.name.starts_with("msg2") {
// tx.send(Err(dubbo::status::Status::internal(format!("err: args is invalid, {:?}", v.name))
// )).await.expect("working rx");
// continue;
// }
tx.send(Ok(EchoResponse {
message: format!("server reply: {:?}", v.message),
}))
.await
.expect("working rx")
}
Err(err) => {
if let Some(io_err) = match_for_io_error(&err) {
if io_err.kind() == ErrorKind::BrokenPipe {
// here you can handle special case when client
// disconnected in unexpected way
eprintln!("\tclient disconnected: broken pipe");
break;
}
}
match tx.send(Err(err)).await {
Ok(_) => (),
Err(_err) => break, // response was droped
}
}
}
}
println!("\tstream ended");
});
// echo just write the same data that was received
let out_stream = ReceiverStream::new(rx);
Ok(Response::new(
Box::pin(out_stream) as Self::BidirectionalStreamingEchoStream
))
}
}
fn match_for_io_error(err_status: &dubbo::status::Status) -> Option<&std::io::Error> {
let mut err: &(dyn std::error::Error + 'static) = err_status;
loop {
if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
return Some(io_err);
}
err = match err.source() {
Some(err) => err,
None => return None,
};
}
}