chore: Reorganize examples and interop crates (#180)

* chore: Reorganize examples and interop crates

* fix interop tests
This commit is contained in:
Lucio Franco
2019-12-12 11:53:15 -05:00
committed by GitHub
parent f096a238ac
commit d9a481baef
69 changed files with 25 additions and 22 deletions
+130
View File
@@ -0,0 +1,130 @@
use std::time::Duration;
use structopt::{clap::arg_enum, StructOpt};
use tonic::transport::Endpoint;
use tonic::transport::{Certificate, ClientTlsConfig};
use tonic_interop::client;
#[derive(StructOpt)]
struct Opts {
#[structopt(
long = "test_case",
use_delimiter = true,
min_values = 1,
raw(possible_values = r#"&Testcase::variants()"#)
)]
test_case: Vec<Testcase>,
#[structopt(long)]
use_tls: bool,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_interop::trace_init();
let matches = Opts::from_args();
let test_cases = matches.test_case;
#[allow(unused_mut)]
let mut endpoint = Endpoint::from_static("http://localhost:10000")
.timeout(Duration::from_secs(5))
.concurrency_limit(30);
if matches.use_tls {
let pem = tokio::fs::read("interop/data/ca.pem").await?;
let ca = Certificate::from_pem(pem);
endpoint = endpoint.tls_config(
ClientTlsConfig::with_rustls()
.ca_certificate(ca)
.domain_name("foo.test.google.fr"),
);
}
let channel = endpoint.connect().await?;
let mut client = client::TestClient::new(channel.clone());
let mut unimplemented_client = client::UnimplementedClient::new(channel);
let mut failures = Vec::new();
for test_case in test_cases {
println!("{:?}:", test_case);
let mut test_results = Vec::new();
match test_case {
Testcase::empty_unary => client::empty_unary(&mut client, &mut test_results).await,
Testcase::large_unary => client::large_unary(&mut client, &mut test_results).await,
Testcase::client_streaming => {
client::client_streaming(&mut client, &mut test_results).await
}
Testcase::server_streaming => {
client::server_streaming(&mut client, &mut test_results).await
}
Testcase::ping_pong => client::ping_pong(&mut client, &mut test_results).await,
Testcase::empty_stream => client::empty_stream(&mut client, &mut test_results).await,
Testcase::status_code_and_message => {
client::status_code_and_message(&mut client, &mut test_results).await
}
Testcase::special_status_message => {
client::special_status_message(&mut client, &mut test_results).await
}
Testcase::unimplemented_method => {
client::unimplemented_method(&mut client, &mut test_results).await
}
Testcase::unimplemented_service => {
client::unimplemented_service(&mut unimplemented_client, &mut test_results).await
}
Testcase::custom_metadata => {
client::custom_metadata(&mut client, &mut test_results).await
}
_ => unimplemented!(),
}
for result in test_results {
println!(" {}", result);
if result.is_failed() {
failures.push(result);
}
}
}
if !failures.is_empty() {
println!("{} tests failed", failures.len());
std::process::exit(1);
}
Ok(())
}
arg_enum! {
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
enum Testcase {
empty_unary,
cacheable_unary,
large_unary,
client_compressed_unary,
server_compressed_unary,
client_streaming,
client_compressed_streaming,
server_streaming,
server_compressed_streaming,
ping_pong,
empty_stream,
compute_engine_creds,
jwt_token_creds,
oauth2_auth_token,
per_rpc_creds,
custom_metadata,
status_code_and_message,
special_status_message,
unimplemented_method,
unimplemented_service,
cancel_after_begin,
cancel_after_first_response,
timeout_on_sleeping_server,
concurrent_large_unary
}
}
+70
View File
@@ -0,0 +1,70 @@
use http::header::HeaderName;
use structopt::StructOpt;
use tonic::body::BoxBody;
use tonic::client::GrpcService;
use tonic::transport::Server;
use tonic::transport::{Identity, ServerTlsConfig};
use tonic_interop::{server, MergeTrailers};
#[derive(StructOpt)]
struct Opts {
#[structopt(long)]
use_tls: bool,
}
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
tonic_interop::trace_init();
let matches = Opts::from_args();
let addr = "127.0.0.1:10000".parse().unwrap();
let mut builder = Server::builder().interceptor_fn(|svc, req| {
let echo_header = req
.headers()
.get("x-grpc-test-echo-initial")
.map(Clone::clone);
let echo_trailer = req
.headers()
.get("x-grpc-test-echo-trailing-bin")
.map(Clone::clone)
.map(|v| (HeaderName::from_static("x-grpc-test-echo-trailing-bin"), v));
let call = svc.call(req);
async move {
let mut res = call.await?;
if let Some(echo_header) = echo_header {
res.headers_mut()
.insert("x-grpc-test-echo-initial", echo_header);
}
Ok(res
.map(|b| MergeTrailers::new(b, echo_trailer))
.map(BoxBody::new))
}
});
if matches.use_tls {
let cert = tokio::fs::read("interop/data/server1.pem").await?;
let key = tokio::fs::read("interop/data/server1.key").await?;
let identity = Identity::from_pem(cert, key);
builder = builder.tls_config(ServerTlsConfig::with_rustls().identity(identity));
}
let test_service = server::TestServiceServer::new(server::TestService::default());
let unimplemented_service =
server::UnimplementedServiceServer::new(server::UnimplementedService::default());
builder
.add_service(test_service)
.add_service(unimplemented_service)
.serve(addr)
.await?;
Ok(())
}
+409
View File
@@ -0,0 +1,409 @@
use crate::{
pb::testservice_client::*, pb::unimplementedservice_client::*, pb::*, test_assert,
TestAssertion,
};
use futures_util::{future, stream, StreamExt};
use tokio::sync::mpsc;
use tonic::transport::Channel;
use tonic::{metadata::MetadataValue, Code, Request, Response, Status};
pub type TestClient = TestServiceClient<Channel>;
pub type UnimplementedClient = UnimplementedServiceClient<Channel>;
const LARGE_REQ_SIZE: usize = 271828;
const LARGE_RSP_SIZE: i32 = 314159;
const REQUEST_LENGTHS: &'static [i32] = &[27182, 8, 1828, 45904];
const RESPONSE_LENGTHS: &'static [i32] = &[31415, 9, 2653, 58979];
const TEST_STATUS_MESSAGE: &'static str = "test status message";
const SPECIAL_TEST_STATUS_MESSAGE: &'static str =
"\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n";
pub async fn empty_unary(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let result = client.empty_call(Request::new(Empty {})).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(response) = result {
let body = response.into_inner();
assertions.push(test_assert!(
"body must not be null",
body == Empty {},
format!("body={:?}", body)
));
}
}
pub async fn large_unary(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
use std::mem;
let payload = crate::client_payload(LARGE_REQ_SIZE);
let req = SimpleRequest {
response_type: PayloadType::Compressable as i32,
response_size: LARGE_RSP_SIZE,
payload: Some(payload),
..Default::default()
};
let result = client.unary_call(Request::new(req)).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(response) = result {
let body = response.into_inner();
let payload_len = body.payload.as_ref().map(|p| p.body.len()).unwrap_or(0);
assertions.push(test_assert!(
"body must be 314159 bytes",
payload_len == LARGE_RSP_SIZE as usize,
format!("mem::size_of_val(&body)={:?}", mem::size_of_val(&body))
));
}
}
// pub async fn cachable_unary(client: &mut Client, assertions: &mut Vec<TestAssertion>) {
// let payload = Payload {
// r#type: PayloadType::Compressable as i32,
// body: format!("{:?}", std::time::Instant::now()).into_bytes(),
// };
// let req = SimpleRequest {
// response_type: PayloadType::Compressable as i32,
// payload: Some(payload),
// ..Default::default()
// };
// client.
// }
pub async fn client_streaming(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let requests = REQUEST_LENGTHS.iter().map(|len| StreamingInputCallRequest {
payload: Some(crate::client_payload(*len as usize)),
..Default::default()
});
let stream = stream::iter(requests);
let result = client.streaming_input_call(Request::new(stream)).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(response) = result {
let body = response.into_inner();
assertions.push(test_assert!(
"aggregated payload size must be 74922 bytes",
body.aggregated_payload_size == 74922,
format!("aggregated_payload_size={:?}", body.aggregated_payload_size)
));
}
}
pub async fn server_streaming(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let req = StreamingOutputCallRequest {
response_parameters: RESPONSE_LENGTHS
.iter()
.map(|len| ResponseParameters::with_size(*len))
.collect(),
..Default::default()
};
let req = Request::new(req);
let result = client.streaming_output_call(req).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(response) = result {
let responses = response
.into_inner()
.filter_map(|m| future::ready(m.ok()))
.collect::<Vec<_>>()
.await;
let actual_response_lengths = crate::response_lengths(&responses);
let asserts = vec![
test_assert!(
"there should be four responses",
responses.len() == 4,
format!("responses.len()={:?}", responses.len())
),
test_assert!(
"the response payload sizes should match input",
RESPONSE_LENGTHS == actual_response_lengths.as_slice(),
format!("{:?}={:?}", RESPONSE_LENGTHS, actual_response_lengths)
),
];
assertions.extend(asserts);
}
}
pub async fn ping_pong(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let (tx, rx) = mpsc::unbounded_channel();
tx.send(make_ping_pong_request(0)).unwrap();
let result = client.full_duplex_call(Request::new(rx)).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(mut response) = result.map(Response::into_inner) {
let mut responses = Vec::new();
loop {
match response.next().await {
Some(result) => {
responses.push(result.unwrap());
if responses.len() == REQUEST_LENGTHS.len() {
drop(tx);
break;
} else {
tx.send(make_ping_pong_request(responses.len())).unwrap();
}
}
None => {
assertions.push(TestAssertion::Failed {
description:
"server should keep the stream open until the client closes it",
expression: "Stream terminated unexpectedly early",
why: None,
});
break;
}
}
}
let actual_response_lengths = crate::response_lengths(&responses);
assertions.push(test_assert!(
"there should be four responses",
responses.len() == RESPONSE_LENGTHS.len(),
format!("{:?}={:?}", responses.len(), RESPONSE_LENGTHS.len())
));
assertions.push(test_assert!(
"the response payload sizes should match input",
RESPONSE_LENGTHS == actual_response_lengths.as_slice(),
format!("{:?}={:?}", RESPONSE_LENGTHS, actual_response_lengths)
));
}
}
pub async fn empty_stream(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let stream = stream::iter(Vec::new());
let result = client.full_duplex_call(Request::new(stream)).await;
assertions.push(test_assert!(
"call must be successful",
result.is_ok(),
format!("result={:?}", result)
));
if let Ok(response) = result.map(Response::into_inner) {
let responses = response.collect::<Vec<_>>().await;
assertions.push(test_assert!(
"there should be no responses",
responses.len() == 0,
format!("responses.len()={:?}", responses.len())
));
}
}
pub async fn status_code_and_message(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
fn validate_response<T>(result: Result<T, Status>, assertions: &mut Vec<TestAssertion>)
where
T: std::fmt::Debug,
{
assertions.push(test_assert!(
"call must fail with unknown status code",
match &result {
Err(status) => status.code() == Code::Unknown,
_ => false,
},
format!("result={:?}", result)
));
assertions.push(test_assert!(
"call must respsond with expected status message",
match &result {
Err(status) => status.message() == TEST_STATUS_MESSAGE,
_ => false,
},
format!("result={:?}", result)
));
}
let simple_req = SimpleRequest {
response_status: Some(EchoStatus {
code: 2,
message: TEST_STATUS_MESSAGE.to_string(),
..Default::default()
}),
..Default::default()
};
let duplex_req = StreamingOutputCallRequest {
response_status: Some(EchoStatus {
code: 2,
message: TEST_STATUS_MESSAGE.to_string(),
..Default::default()
}),
..Default::default()
};
let result = client.unary_call(Request::new(simple_req)).await;
validate_response(result, assertions);
let stream = stream::iter(vec![duplex_req]);
let result = match client.full_duplex_call(Request::new(stream)).await {
Ok(response) => {
let stream = response.into_inner();
let responses = stream.collect::<Vec<_>>().await;
Ok(responses)
}
Err(e) => Err(e),
};
validate_response(result, assertions);
}
pub async fn special_status_message(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let req = SimpleRequest {
response_status: Some(EchoStatus {
code: 2,
message: SPECIAL_TEST_STATUS_MESSAGE.to_string(),
..Default::default()
}),
..Default::default()
};
let result = client.unary_call(Request::new(req)).await;
assertions.push(test_assert!(
"call must fail with unknown status code",
match &result {
Err(status) => status.code() == Code::Unknown,
_ => false,
},
format!("result={:?}", result)
));
assertions.push(test_assert!(
"call must respsond with expected status message",
match &result {
Err(status) => status.message() == SPECIAL_TEST_STATUS_MESSAGE,
_ => false,
},
format!("result={:?}", result)
));
}
pub async fn unimplemented_method(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let result = client.unimplemented_call(Request::new(Empty {})).await;
assertions.push(test_assert!(
"call must fail with unimplemented status code",
match &result {
Err(status) => status.code() == Code::Unimplemented,
_ => false,
},
format!("result={:?}", result)
));
}
pub async fn unimplemented_service(
client: &mut UnimplementedClient,
assertions: &mut Vec<TestAssertion>,
) {
let result = client.unimplemented_call(Request::new(Empty {})).await;
assertions.push(test_assert!(
"call must fail with unimplemented status code",
match &result {
Err(status) => status.code() == Code::Unimplemented,
_ => false,
},
format!("result={:?}", result)
));
}
pub async fn custom_metadata(client: &mut TestClient, assertions: &mut Vec<TestAssertion>) {
let key1 = "x-grpc-test-echo-initial";
let value1 = MetadataValue::from_str("test_initial_metadata_value").unwrap();
let key2 = "x-grpc-test-echo-trailing-bin";
let value2 = MetadataValue::from_bytes(&[0xab, 0xab, 0xab]);
let req = SimpleRequest {
response_type: PayloadType::Compressable as i32,
response_size: LARGE_RSP_SIZE,
payload: Some(crate::client_payload(LARGE_REQ_SIZE)),
..Default::default()
};
let mut req_unary = Request::new(req);
req_unary.metadata_mut().insert(key1, value1.clone());
req_unary.metadata_mut().insert_bin(key2, value2.clone());
let stream = stream::iter(vec![make_ping_pong_request(0)]);
let mut req_stream = Request::new(stream);
req_stream.metadata_mut().insert(key1, value1.clone());
req_stream.metadata_mut().insert_bin(key2, value2.clone());
let response = client
.unary_call(req_unary)
.await
.expect("call should pass.");
assertions.push(test_assert!(
"metadata string must match in unary",
response.metadata().get(key1) == Some(&value1),
format!("result={:?}", response.metadata().get(key1))
));
assertions.push(test_assert!(
"metadata bin must match in unary",
response.metadata().get_bin(key2) == Some(&value2),
format!("result={:?}", response.metadata().get_bin(key1))
));
let response = client
.full_duplex_call(req_stream)
.await
.expect("call should pass.");
assertions.push(test_assert!(
"metadata string must match in unary",
response.metadata().get(key1) == Some(&value1),
format!("result={:?}", response.metadata().get(key1))
));
let mut stream = response.into_inner();
let trailers = stream.trailers().await.unwrap().unwrap();
assertions.push(test_assert!(
"metadata bin must match in unary",
trailers.get_bin(key2) == Some(&value2),
format!("result={:?}", trailers.get_bin(key1))
));
}
fn make_ping_pong_request(idx: usize) -> StreamingOutputCallRequest {
let req_len = REQUEST_LENGTHS[idx];
let resp_len = RESPONSE_LENGTHS[idx];
StreamingOutputCallRequest {
response_parameters: vec![ResponseParameters::with_size(resp_len)],
payload: Some(crate::client_payload(req_len as usize)),
..Default::default()
}
}
+187
View File
@@ -0,0 +1,187 @@
#![recursion_limit = "256"]
pub mod client;
pub mod server;
pub mod pb {
#![allow(dead_code)]
#![allow(unused_imports)]
include!(concat!(env!("OUT_DIR"), "/grpc.testing.rs"));
}
use http::header::{HeaderMap, HeaderName, HeaderValue};
use http_body::Body;
use std::{
default, fmt, iter,
pin::Pin,
task::{Context, Poll},
};
pub fn trace_init() {
let sub = tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(tracing_subscriber::filter::EnvFilter::from_default_env())
.finish();
let _ = tracing::subscriber::set_global_default(sub);
let _ = tracing_log::LogTracer::init();
}
pub fn client_payload(size: usize) -> pb::Payload {
pb::Payload {
r#type: default::Default::default(),
body: iter::repeat(0u8).take(size).collect(),
}
}
pub fn server_payload(size: usize) -> pb::Payload {
pb::Payload {
r#type: default::Default::default(),
body: iter::repeat(0u8).take(size).collect(),
}
}
impl pb::ResponseParameters {
fn with_size(size: i32) -> Self {
pb::ResponseParameters {
size,
..Default::default()
}
}
}
fn response_length(response: &pb::StreamingOutputCallResponse) -> i32 {
match &response.payload {
Some(ref payload) => payload.body.len() as i32,
None => 0,
}
}
fn response_lengths(responses: &Vec<pb::StreamingOutputCallResponse>) -> Vec<i32> {
responses.iter().map(&response_length).collect()
}
#[derive(Debug)]
pub enum TestAssertion {
Passed {
description: &'static str,
},
Failed {
description: &'static str,
expression: &'static str,
why: Option<String>,
},
}
impl TestAssertion {
pub fn is_failed(&self) -> bool {
match self {
TestAssertion::Failed { .. } => true,
_ => false,
}
}
}
impl fmt::Display for TestAssertion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use console::{style, Emoji};
match *self {
TestAssertion::Passed { ref description } => write!(
f,
"{check} {desc}",
check = style(Emoji("", "+")).green(),
desc = style(description).green(),
),
TestAssertion::Failed {
ref description,
ref expression,
why: Some(ref why),
} => write!(
f,
"{check} {desc}\n in `{exp}`: {why}",
check = style(Emoji("", "x")).red(),
desc = style(description).red(),
exp = style(expression).red(),
why = style(why).red(),
),
TestAssertion::Failed {
ref description,
ref expression,
why: None,
} => write!(
f,
"{check} {desc}\n in `{exp}`",
check = style(Emoji("", "x")).red(),
desc = style(description).red(),
exp = style(expression).red(),
),
}
}
}
#[macro_export]
macro_rules! test_assert {
($description:expr, $assertion:expr) => {
if $assertion {
crate::TestAssertion::Passed {
description: $description,
}
} else {
TestAssertion::Failed {
description: $description,
expression: stringify!($assertion),
why: None,
}
}
};
($description:expr, $assertion:expr, $why:expr) => {
if $assertion {
crate::TestAssertion::Passed {
description: $description,
}
} else {
crate::TestAssertion::Failed {
description: $description,
expression: stringify!($assertion),
why: Some($why),
}
}
};
}
pub struct MergeTrailers<B> {
inner: B,
trailer: Option<(HeaderName, HeaderValue)>,
}
impl<B> MergeTrailers<B> {
pub fn new(inner: B, trailer: Option<(HeaderName, HeaderValue)>) -> Self {
Self { inner, trailer }
}
}
impl<B: Body + Unpin> Body for MergeTrailers<B> {
type Data = B::Data;
type Error = B::Error;
fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
Pin::new(&mut self.inner).poll_data(cx)
}
fn poll_trailers(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
Pin::new(&mut self.inner).poll_trailers(cx).map_ok(|h| {
h.map(|mut headers| {
if let Some((key, value)) = &self.trailer {
headers.insert(key.clone(), value.clone());
}
headers
})
})
}
}
+161
View File
@@ -0,0 +1,161 @@
use crate::pb::{self, *};
use async_stream::try_stream;
use futures_util::{stream, StreamExt, TryStreamExt};
use std::pin::Pin;
use std::time::Duration;
use tonic::{Code, Request, Response, Status};
pub use pb::testservice_server::TestServiceServer;
pub use pb::unimplementedservice_server::UnimplementedServiceServer;
#[derive(Default, Clone)]
pub struct TestService;
type Result<T> = std::result::Result<Response<T>, Status>;
type Streaming<T> = Request<tonic::Streaming<T>>;
type Stream<T> = Pin<
Box<dyn futures_core::Stream<Item = std::result::Result<T, Status>> + Send + Sync + 'static>,
>;
#[tonic::async_trait]
impl pb::testservice_server::TestService for TestService {
async fn empty_call(&self, _request: Request<Empty>) -> Result<Empty> {
Ok(Response::new(Empty {}))
}
async fn unary_call(&self, request: Request<SimpleRequest>) -> Result<SimpleResponse> {
let req = request.into_inner();
if let Some(echo_status) = req.response_status {
let status = Status::new(Code::from_i32(echo_status.code), echo_status.message);
return Err(status);
}
let res_size = if req.response_size >= 0 {
req.response_size as usize
} else {
let status = Status::new(Code::InvalidArgument, "response_size cannot be negative");
return Err(status);
};
let res = SimpleResponse {
payload: Some(Payload {
body: vec![0; res_size],
..Default::default()
}),
..Default::default()
};
Ok(Response::new(res))
}
async fn cacheable_unary_call(&self, _: Request<SimpleRequest>) -> Result<SimpleResponse> {
unimplemented!()
}
type StreamingOutputCallStream = Stream<StreamingOutputCallResponse>;
async fn streaming_output_call(
&self,
req: Request<StreamingOutputCallRequest>,
) -> Result<Self::StreamingOutputCallStream> {
let StreamingOutputCallRequest {
response_parameters,
..
} = req.into_inner();
let stream = try_stream! {
for param in response_parameters {
tokio::time::delay_for(Duration::from_micros(param.interval_us as u64)).await;
let payload = crate::server_payload(param.size as usize);
yield StreamingOutputCallResponse { payload: Some(payload) };
}
};
Ok(Response::new(
Box::pin(stream) as Self::StreamingOutputCallStream
))
}
async fn streaming_input_call(
&self,
req: Streaming<StreamingInputCallRequest>,
) -> Result<StreamingInputCallResponse> {
let mut stream = req.into_inner();
let mut aggregated_payload_size = 0 as i32;
while let Some(msg) = stream.try_next().await? {
aggregated_payload_size += msg.payload.unwrap().body.len() as i32;
}
let res = StreamingInputCallResponse {
aggregated_payload_size,
};
Ok(Response::new(res))
}
type FullDuplexCallStream = Stream<StreamingOutputCallResponse>;
async fn full_duplex_call(
&self,
req: Streaming<StreamingOutputCallRequest>,
) -> Result<Self::FullDuplexCallStream> {
let mut stream = req.into_inner();
if let Some(first_msg) = stream.message().await? {
if let Some(echo_status) = first_msg.response_status {
let status = Status::new(Code::from_i32(echo_status.code), echo_status.message);
return Err(status);
}
let single_message = stream::iter(vec![Ok(first_msg)]);
let mut stream = single_message.chain(stream);
let stream = try_stream! {
while let Some(msg) = stream.try_next().await? {
if let Some(echo_status) = msg.response_status {
let status = Status::new(Code::from_i32(echo_status.code), echo_status.message);
Err(status)?;
}
for param in msg.response_parameters {
tokio::time::delay_for(Duration::from_micros(param.interval_us as u64)).await;
let payload = crate::server_payload(param.size as usize);
yield StreamingOutputCallResponse { payload: Some(payload) };
}
}
};
Ok(Response::new(Box::pin(stream) as Self::FullDuplexCallStream))
} else {
let stream = stream::empty();
Ok(Response::new(Box::pin(stream) as Self::FullDuplexCallStream))
}
}
type HalfDuplexCallStream = Stream<StreamingOutputCallResponse>;
async fn half_duplex_call(
&self,
_: Streaming<StreamingOutputCallRequest>,
) -> Result<Self::HalfDuplexCallStream> {
Err(Status::unimplemented("TODO"))
}
async fn unimplemented_call(&self, _: Request<Empty>) -> Result<Empty> {
Err(Status::unimplemented(""))
}
}
#[derive(Default)]
pub struct UnimplementedService;
#[tonic::async_trait]
impl pb::unimplementedservice_server::UnimplementedService for UnimplementedService {
async fn unimplemented_call(&self, _req: Request<Empty>) -> Result<Empty> {
Err(Status::unimplemented(""))
}
}