@@ -3,17 +3,39 @@ pub mod pb {
33}
44
55use futures:: Stream ;
6- use std:: net:: ToSocketAddrs ;
7- use std:: pin:: Pin ;
8- use std:: task:: { Context , Poll } ;
9- use tokio:: sync:: oneshot;
6+ use std:: { error:: Error , io:: ErrorKind , net:: ToSocketAddrs , pin:: Pin , time:: Duration } ;
7+ use tokio:: sync:: mpsc;
8+ use tokio_stream:: { wrappers:: ReceiverStream , StreamExt } ;
109use tonic:: { transport:: Server , Request , Response , Status , Streaming } ;
1110
1211use pb:: { EchoRequest , EchoResponse } ;
1312
1413type EchoResult < T > = Result < Response < T > , Status > ;
1514type ResponseStream = Pin < Box < dyn Stream < Item = Result < EchoResponse , Status > > + Send > > ;
1615
16+ fn match_for_io_error ( err_status : & Status ) -> Option < & std:: io:: Error > {
17+ let mut err: & ( dyn Error + ' static ) = err_status;
18+
19+ loop {
20+ if let Some ( io_err) = err. downcast_ref :: < std:: io:: Error > ( ) {
21+ return Some ( io_err) ;
22+ }
23+
24+ // h2::Error do not expose std::io::Error with `source()`
25+ // https://github.com/hyperium/h2/pull/462
26+ if let Some ( h2_err) = err. downcast_ref :: < h2:: Error > ( ) {
27+ if let Some ( io_err) = h2_err. get_io ( ) {
28+ return Some ( io_err) ;
29+ }
30+ }
31+
32+ err = match err. source ( ) {
33+ Some ( err) => err,
34+ None => return None ,
35+ } ;
36+ }
37+ }
38+
1739#[ derive( Debug ) ]
1840pub struct EchoServer { }
1941
@@ -29,28 +51,36 @@ impl pb::echo_server::Echo for EchoServer {
2951 & self ,
3052 req : Request < EchoRequest > ,
3153 ) -> EchoResult < Self :: ServerStreamingEchoStream > {
32- println ! ( "Client connected from: {:?}" , req. remote_addr( ) ) ;
54+ println ! ( "EchoServer::server_streaming_echo" ) ;
55+ println ! ( "\t client connected from: {:?}" , req. remote_addr( ) ) ;
3356
34- let ( tx, rx) = oneshot:: channel :: < ( ) > ( ) ;
35-
36- tokio:: spawn ( async move {
37- let _ = rx. await ;
38- println ! ( "The rx resolved therefore the client disconnected!" ) ;
57+ // creating infinite stream with requested message
58+ let repeat = std:: iter:: repeat ( EchoResponse {
59+ message : req. into_inner ( ) . message ,
3960 } ) ;
61+ let mut stream = Box :: pin ( tokio_stream:: iter ( repeat) . throttle ( Duration :: from_millis ( 200 ) ) ) ;
4062
41- struct ClientDisconnect ( oneshot:: Sender < ( ) > ) ;
42-
43- impl Stream for ClientDisconnect {
44- type Item = Result < EchoResponse , Status > ;
45-
46- fn poll_next ( self : Pin < & mut Self > , _: & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
47- // A stream that never resolves to anything....
48- Poll :: Pending
63+ // spawn and channel are required if you want handle "disconnect" functionality
64+ // the `out_stream` will not be polled after client disconnect
65+ let ( tx, rx) = mpsc:: channel ( 128 ) ;
66+ tokio:: spawn ( async move {
67+ while let Some ( item) = stream. next ( ) . await {
68+ match tx. send ( Result :: < _ , Status > :: Ok ( item) ) . await {
69+ Ok ( _) => {
70+ // item (server response) was queued to be send to client
71+ }
72+ Err ( _item) => {
73+ // output_stream was build from rx and both are dropped
74+ break ;
75+ }
76+ }
4977 }
50- }
78+ println ! ( "\t client disconnected" ) ;
79+ } ) ;
5180
81+ let output_stream = ReceiverStream :: new ( rx) ;
5282 Ok ( Response :: new (
53- Box :: pin ( ClientDisconnect ( tx ) ) as Self :: ServerStreamingEchoStream
83+ Box :: pin ( output_stream ) as Self :: ServerStreamingEchoStream
5484 ) )
5585 }
5686
@@ -65,9 +95,50 @@ impl pb::echo_server::Echo for EchoServer {
6595
6696 async fn bidirectional_streaming_echo (
6797 & self ,
68- _ : Request < Streaming < EchoRequest > > ,
98+ req : Request < Streaming < EchoRequest > > ,
6999 ) -> EchoResult < Self :: BidirectionalStreamingEchoStream > {
70- Err ( Status :: unimplemented ( "not implemented" ) )
100+ println ! ( "EchoServer::bidirectional_streaming_echo" ) ;
101+
102+ let mut in_stream = req. into_inner ( ) ;
103+ let ( tx, rx) = mpsc:: channel ( 128 ) ;
104+
105+ // this spawn here is required if you want to handle connection error.
106+ // If we just map `in_stream` and write it back as `out_stream` the `out_stream`
107+ // will be drooped when connection error occurs and error will never be propagated
108+ // to mapped version of `in_stream`.
109+ tokio:: spawn ( async move {
110+ while let Some ( result) = in_stream. next ( ) . await {
111+ match result {
112+ Ok ( v) => tx
113+ . send ( Ok ( EchoResponse { message : v. message } ) )
114+ . await
115+ . expect ( "working rx" ) ,
116+ Err ( err) => {
117+ if let Some ( io_err) = match_for_io_error ( & err) {
118+ if io_err. kind ( ) == ErrorKind :: BrokenPipe {
119+ // here you can handle special case when client
120+ // disconnected in unexpected way
121+ eprintln ! ( "\t client disconnected: broken pipe" ) ;
122+ break ;
123+ }
124+ }
125+
126+ match tx. send ( Err ( err) ) . await {
127+ Ok ( _) => ( ) ,
128+ Err ( _err) => break , // response was droped
129+ }
130+ }
131+ }
132+ }
133+ println ! ( "\t stream ended" ) ;
134+ } ) ;
135+
136+ // echo just write the same data that was received
137+ let out_stream = ReceiverStream :: new ( rx) ;
138+
139+ Ok ( Response :: new (
140+ Box :: pin ( out_stream) as Self :: BidirectionalStreamingEchoStream
141+ ) )
71142 }
72143}
73144
0 commit comments