diff --git a/tonic-examples/Cargo.toml b/tonic-examples/Cargo.toml index d87a33e..92eddf1 100644 --- a/tonic-examples/Cargo.toml +++ b/tonic-examples/Cargo.toml @@ -26,6 +26,9 @@ tokio = "=0.2.0-alpha.1" prost = "0.5" prost-derive = "0.5" bytes = "0.4" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +async-stream = "0.1" [build-dependencies] tonic-build = { path = "../tonic-build" } diff --git a/tonic-examples/data/route_guide_db.json b/tonic-examples/data/route_guide_db.json new file mode 100644 index 0000000..22e93e3 --- /dev/null +++ b/tonic-examples/data/route_guide_db.json @@ -0,0 +1,702 @@ +[ + { + "location": { + "latitude": 407838351, + "longitude": -746143763 + }, + "name": "Patriots Path, Mendham, NJ 07945, USA" + }, + { + "location": { + "latitude": 408122808, + "longitude": -743999179 + }, + "name": "101 New Jersey 10, Whippany, NJ 07981, USA" + }, + { + "location": { + "latitude": 413628156, + "longitude": -749015468 + }, + "name": "U.S. 6, Shohola, PA 18458, USA" + }, + { + "location": { + "latitude": 419999544, + "longitude": -740371136 + }, + "name": "5 Conners Road, Kingston, NY 12401, USA" + }, + { + "location": { + "latitude": 414008389, + "longitude": -743951297 + }, + "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA" + }, + { + "location": { + "latitude": 419611318, + "longitude": -746524769 + }, + "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA" + }, + { + "location": { + "latitude": 406109563, + "longitude": -742186778 + }, + "name": "4001 Tremley Point Road, Linden, NJ 07036, USA" + }, + { + "location": { + "latitude": 416802456, + "longitude": -742370183 + }, + "name": "352 South Mountain Road, Wallkill, NY 12589, USA" + }, + { + "location": { + "latitude": 412950425, + "longitude": -741077389 + }, + "name": "Bailey Turn Road, Harriman, NY 10926, USA" + }, + { + "location": { + "latitude": 412144655, + "longitude": -743949739 + }, + "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA" + }, + { + "location": { + "latitude": 415736605, + "longitude": -742847522 + }, + "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA" + }, + { + "location": { + "latitude": 413843930, + "longitude": -740501726 + }, + "name": "162 Merrill Road, Highland Mills, NY 10930, USA" + }, + { + "location": { + "latitude": 410873075, + "longitude": -744459023 + }, + "name": "Clinton Road, West Milford, NJ 07480, USA" + }, + { + "location": { + "latitude": 412346009, + "longitude": -744026814 + }, + "name": "16 Old Brook Lane, Warwick, NY 10990, USA" + }, + { + "location": { + "latitude": 402948455, + "longitude": -747903913 + }, + "name": "3 Drake Lane, Pennington, NJ 08534, USA" + }, + { + "location": { + "latitude": 406337092, + "longitude": -740122226 + }, + "name": "6324 8th Avenue, Brooklyn, NY 11220, USA" + }, + { + "location": { + "latitude": 406421967, + "longitude": -747727624 + }, + "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA" + }, + { + "location": { + "latitude": 416318082, + "longitude": -749677716 + }, + "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA" + }, + { + "location": { + "latitude": 415301720, + "longitude": -748416257 + }, + "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA" + }, + { + "location": { + "latitude": 402647019, + "longitude": -747071791 + }, + "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA" + }, + { + "location": { + "latitude": 412567807, + "longitude": -741058078 + }, + "name": "New York State Reference Route 987E, Southfields, NY 10975, USA" + }, + { + "location": { + "latitude": 416855156, + "longitude": -744420597 + }, + "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA" + }, + { + "location": { + "latitude": 404663628, + "longitude": -744820157 + }, + "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA" + }, + { + "location": { + "latitude": 407113723, + "longitude": -749746483 + }, + "name": "" + }, + { + "location": { + "latitude": 402133926, + "longitude": -743613249 + }, + "name": "" + }, + { + "location": { + "latitude": 400273442, + "longitude": -741220915 + }, + "name": "" + }, + { + "location": { + "latitude": 411236786, + "longitude": -744070769 + }, + "name": "" + }, + { + "location": { + "latitude": 411633782, + "longitude": -746784970 + }, + "name": "211-225 Plains Road, Augusta, NJ 07822, USA" + }, + { + "location": { + "latitude": 415830701, + "longitude": -742952812 + }, + "name": "" + }, + { + "location": { + "latitude": 413447164, + "longitude": -748712898 + }, + "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA" + }, + { + "location": { + "latitude": 405047245, + "longitude": -749800722 + }, + "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA" + }, + { + "location": { + "latitude": 418858923, + "longitude": -746156790 + }, + "name": "" + }, + { + "location": { + "latitude": 417951888, + "longitude": -748484944 + }, + "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA" + }, + { + "location": { + "latitude": 407033786, + "longitude": -743977337 + }, + "name": "26 East 3rd Street, New Providence, NJ 07974, USA" + }, + { + "location": { + "latitude": 417548014, + "longitude": -740075041 + }, + "name": "" + }, + { + "location": { + "latitude": 410395868, + "longitude": -744972325 + }, + "name": "" + }, + { + "location": { + "latitude": 404615353, + "longitude": -745129803 + }, + "name": "" + }, + { + "location": { + "latitude": 406589790, + "longitude": -743560121 + }, + "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA" + }, + { + "location": { + "latitude": 414653148, + "longitude": -740477477 + }, + "name": "18 Lannis Avenue, New Windsor, NY 12553, USA" + }, + { + "location": { + "latitude": 405957808, + "longitude": -743255336 + }, + "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA" + }, + { + "location": { + "latitude": 411733589, + "longitude": -741648093 + }, + "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA" + }, + { + "location": { + "latitude": 412676291, + "longitude": -742606606 + }, + "name": "1270 Lakes Road, Monroe, NY 10950, USA" + }, + { + "location": { + "latitude": 409224445, + "longitude": -748286738 + }, + "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA" + }, + { + "location": { + "latitude": 406523420, + "longitude": -742135517 + }, + "name": "652 Garden Street, Elizabeth, NJ 07202, USA" + }, + { + "location": { + "latitude": 401827388, + "longitude": -740294537 + }, + "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA" + }, + { + "location": { + "latitude": 410564152, + "longitude": -743685054 + }, + "name": "13-17 Stanley Street, West Milford, NJ 07480, USA" + }, + { + "location": { + "latitude": 408472324, + "longitude": -740726046 + }, + "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA" + }, + { + "location": { + "latitude": 412452168, + "longitude": -740214052 + }, + "name": "5 White Oak Lane, Stony Point, NY 10980, USA" + }, + { + "location": { + "latitude": 409146138, + "longitude": -746188906 + }, + "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA" + }, + { + "location": { + "latitude": 404701380, + "longitude": -744781745 + }, + "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA" + }, + { + "location": { + "latitude": 409642566, + "longitude": -746017679 + }, + "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA" + }, + { + "location": { + "latitude": 408031728, + "longitude": -748645385 + }, + "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA" + }, + { + "location": { + "latitude": 413700272, + "longitude": -742135189 + }, + "name": "367 Prospect Road, Chester, NY 10918, USA" + }, + { + "location": { + "latitude": 404310607, + "longitude": -740282632 + }, + "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA" + }, + { + "location": { + "latitude": 409319800, + "longitude": -746201391 + }, + "name": "11 Ward Street, Mount Arlington, NJ 07856, USA" + }, + { + "location": { + "latitude": 406685311, + "longitude": -742108603 + }, + "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA" + }, + { + "location": { + "latitude": 419018117, + "longitude": -749142781 + }, + "name": "43 Dreher Road, Roscoe, NY 12776, USA" + }, + { + "location": { + "latitude": 412856162, + "longitude": -745148837 + }, + "name": "Swan Street, Pine Island, NY 10969, USA" + }, + { + "location": { + "latitude": 416560744, + "longitude": -746721964 + }, + "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA" + }, + { + "location": { + "latitude": 405314270, + "longitude": -749836354 + }, + "name": "" + }, + { + "location": { + "latitude": 414219548, + "longitude": -743327440 + }, + "name": "" + }, + { + "location": { + "latitude": 415534177, + "longitude": -742900616 + }, + "name": "565 Winding Hills Road, Montgomery, NY 12549, USA" + }, + { + "location": { + "latitude": 406898530, + "longitude": -749127080 + }, + "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA" + }, + { + "location": { + "latitude": 407586880, + "longitude": -741670168 + }, + "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA" + }, + { + "location": { + "latitude": 400106455, + "longitude": -742870190 + }, + "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA" + }, + { + "location": { + "latitude": 400066188, + "longitude": -746793294 + }, + "name": "" + }, + { + "location": { + "latitude": 418803880, + "longitude": -744102673 + }, + "name": "40 Mountain Road, Napanoch, NY 12458, USA" + }, + { + "location": { + "latitude": 414204288, + "longitude": -747895140 + }, + "name": "" + }, + { + "location": { + "latitude": 414777405, + "longitude": -740615601 + }, + "name": "" + }, + { + "location": { + "latitude": 415464475, + "longitude": -747175374 + }, + "name": "48 North Road, Forestburgh, NY 12777, USA" + }, + { + "location": { + "latitude": 404062378, + "longitude": -746376177 + }, + "name": "" + }, + { + "location": { + "latitude": 405688272, + "longitude": -749285130 + }, + "name": "" + }, + { + "location": { + "latitude": 400342070, + "longitude": -748788996 + }, + "name": "" + }, + { + "location": { + "latitude": 401809022, + "longitude": -744157964 + }, + "name": "" + }, + { + "location": { + "latitude": 404226644, + "longitude": -740517141 + }, + "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA" + }, + { + "location": { + "latitude": 410322033, + "longitude": -747871659 + }, + "name": "" + }, + { + "location": { + "latitude": 407100674, + "longitude": -747742727 + }, + "name": "" + }, + { + "location": { + "latitude": 418811433, + "longitude": -741718005 + }, + "name": "213 Bush Road, Stone Ridge, NY 12484, USA" + }, + { + "location": { + "latitude": 415034302, + "longitude": -743850945 + }, + "name": "" + }, + { + "location": { + "latitude": 411349992, + "longitude": -743694161 + }, + "name": "" + }, + { + "location": { + "latitude": 404839914, + "longitude": -744759616 + }, + "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA" + }, + { + "location": { + "latitude": 414638017, + "longitude": -745957854 + }, + "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA" + }, + { + "location": { + "latitude": 412127800, + "longitude": -740173578 + }, + "name": "" + }, + { + "location": { + "latitude": 401263460, + "longitude": -747964303 + }, + "name": "" + }, + { + "location": { + "latitude": 412843391, + "longitude": -749086026 + }, + "name": "" + }, + { + "location": { + "latitude": 418512773, + "longitude": -743067823 + }, + "name": "" + }, + { + "location": { + "latitude": 404318328, + "longitude": -740835638 + }, + "name": "42-102 Main Street, Belford, NJ 07718, USA" + }, + { + "location": { + "latitude": 419020746, + "longitude": -741172328 + }, + "name": "" + }, + { + "location": { + "latitude": 404080723, + "longitude": -746119569 + }, + "name": "" + }, + { + "location": { + "latitude": 401012643, + "longitude": -744035134 + }, + "name": "" + }, + { + "location": { + "latitude": 404306372, + "longitude": -741079661 + }, + "name": "" + }, + { + "location": { + "latitude": 403966326, + "longitude": -748519297 + }, + "name": "" + }, + { + "location": { + "latitude": 405002031, + "longitude": -748407866 + }, + "name": "" + }, + { + "location": { + "latitude": 409532885, + "longitude": -742200683 + }, + "name": "" + }, + { + "location": { + "latitude": 416851321, + "longitude": -742674555 + }, + "name": "" + }, + { + "location": { + "latitude": 406411633, + "longitude": -741722051 + }, + "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA" + }, + { + "location": { + "latitude": 413069058, + "longitude": -744597778 + }, + "name": "261 Van Sickle Road, Goshen, NY 10924, USA" + }, + { + "location": { + "latitude": 418465462, + "longitude": -746859398 + }, + "name": "" + }, + { + "location": { + "latitude": 411733222, + "longitude": -744228360 + }, + "name": "" + }, + { + "location": { + "latitude": 410248224, + "longitude": -747127767 + }, + "name": "3 Hasta Way, Newton, NJ 07860, USA" + } +] diff --git a/tonic-examples/src/routeguide/data.rs b/tonic-examples/src/routeguide/data.rs new file mode 100644 index 0000000..aa4cd43 --- /dev/null +++ b/tonic-examples/src/routeguide/data.rs @@ -0,0 +1,39 @@ +use serde::Deserialize; +use std::fs::File; +use std::io::prelude::*; + +#[derive(Debug, Deserialize)] +struct Feature { + location: Location, + name: String, +} + +#[derive(Debug, Deserialize)] +struct Location { + latitude: i32, + longitude: i32, +} + +#[allow(dead_code)] +pub fn load() -> Vec { + let mut file = File::open("tonic-examples/data/route_guide_db.json") + .ok() + .expect("failed to open data file"); + let mut data = String::new(); + file.read_to_string(&mut data) + .ok() + .expect("failed to read data file"); + + let decoded: Vec = serde_json::from_str(&data).unwrap(); + + decoded + .into_iter() + .map(|feature| crate::routeguide::Feature { + name: feature.name, + location: Some(crate::routeguide::Point { + longitude: feature.location.longitude, + latitude: feature.location.latitude, + }), + }) + .collect() +} diff --git a/tonic-examples/src/routeguide/server.rs b/tonic-examples/src/routeguide/server.rs index 0c522cf..6abc11e 100644 --- a/tonic-examples/src/routeguide/server.rs +++ b/tonic-examples/src/routeguide/server.rs @@ -1,57 +1,170 @@ #![feature(async_await)] -use futures::Stream; -use tokio::net::TcpListener; +mod data; + +use futures::{Stream, StreamExt}; +use tokio::{net::TcpListener, sync::{mpsc, Lock}}; use tonic::{Request, Response, Status}; use tower_h2::Server; +use std::sync::Arc; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use std::time::Instant; pub mod routeguide { include!(concat!(env!("OUT_DIR"), "/routeguide.rs")); } -use routeguide::*; +use routeguide::{Point, Rectangle, Feature, RouteNote, RouteSummary}; -type BoxStream = Pin> + Send + 'static>>; - -#[derive(Default, Clone)] +#[derive(Debug)] pub struct RouteGuide { - data: String, + state: State, +} + +#[derive(Debug, Clone)] +struct State { + features: Arc>, + notes: Lock>>, } #[tonic::server(service = "routeguide.RouteGuide", proto = "routeguide")] impl RouteGuide { - pub async fn get_feature(&self, _req: Request) -> Result, Status> { - unimplemented!() + pub async fn get_feature(&self, request: Request) -> Result, Status> { + println!("GetFeature = {:?}", request); + + for feature in &self.state.features[..] { + if feature.location.as_ref() == Some(request.get_ref()) { + return Ok(Response::new(feature.clone())); + } + } + + let response = Response::new(Feature { + name: "".to_string(), + location: None, + }); + + Ok(response) } pub async fn list_features( &self, - _req: Request, - ) -> Result>, Status> { - unimplemented!() + request: Request, + ) -> Result>>, Status> { + use std::thread; + + println!("ListFeatures = {:?}", request); + + let (mut tx, rx) = mpsc::channel(4); + + let state = self.state.clone(); + + thread::spawn(move || { + for feature in &state.features[..] { + if in_range(feature.location.as_ref().unwrap(), request.get_ref()) { + println!(" => send {:?}", feature); + tx.try_send(Ok(feature.clone())).unwrap(); + } + } + + println!(" /// done sending"); + }); + + + Ok(Response::new(rx)) } pub async fn record_route( &self, - _req: Request>>, - ) -> Result, Status> { - unimplemented!() + request: Request>>, + ) -> Result, Status> + { + println!("RecordRoute"); + + let stream = request.into_inner(); + + // Pin the inbound stream to the stack so that we can call next on it + futures::pin_mut!(stream); + + let mut summary = RouteSummary::default(); + let mut last_point = None; + let now = Instant::now(); + + while let Some(point) = stream.next().await { + let point = point?; + + println!(" ==> Point = {:?}", point); + + // Increment the point count + summary.point_count += 1; + + // Find features + for feature in &self.state.features[..] { + if feature.location.as_ref() == Some(&point) { + summary.feature_count += 1; + } + } + + // Calculate the distance + if let Some(ref last_point) = last_point { + summary.distance += calc_distance(last_point, &point); + } + + last_point = Some(point); + } + + summary.elapsed_time = now.elapsed().as_secs() as i32; + + Ok(Response::new(summary)) } - // pub async fn route_chat( - // &self, - // _req: Request>>, - // ) -> Result>>, Status> { - // unimplemented!() - // } + pub async fn route_chat( + &self, + request: Request> + Send + 'static>, + ) -> Result> + Send>, Status> { + println!("RouteChat"); + + let stream = request.into_inner(); + let mut state = self.state.clone(); + + + + let output = async_stream::try_stream! { + futures::pin_mut!(stream); + + while let Some(note) = stream.next().await { + let note = note?; + + let location = note.location.clone().unwrap(); + + let mut notes = state.notes.lock().await; + let notes = notes.entry(location).or_insert(vec![]); + notes.push(note); + + for note in notes { + yield note.clone(); + } + } + }; + + Ok(Response::new(output)) + } } #[tokio::main] async fn main() -> Result<(), Box> { - let addr = "[::1]:50051".parse().unwrap(); + let addr = "[::1]:10000".parse().unwrap(); let mut bind = TcpListener::bind(&addr)?; - let route_guide = RouteGuide::default(); + println!("Listening on: {}", bind.local_addr()?); + + let route_guide = RouteGuide { + state: State { + // Load data file + features: Arc::new(data::load()), + notes: Lock::new(HashMap::new()), + }, + }; let mut server = Server::new(RouteGuideServer::new(route_guide), Default::default()); while let Ok((sock, _addr)) = bind.accept().await { @@ -66,3 +179,58 @@ async fn main() -> Result<(), Box> { Ok(()) } + +// Implement hash for Point +impl Hash for Point { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.latitude.hash(state); + self.longitude.hash(state); + } +} + +impl Eq for Point {} + +fn in_range(point: &Point, rect: &Rectangle) -> bool { + use std::cmp; + + let lo = rect.lo.as_ref().unwrap(); + let hi = rect.hi.as_ref().unwrap(); + + let left = cmp::min(lo.longitude, hi.longitude); + let right = cmp::max(lo.longitude, hi.longitude); + let top = cmp::max(lo.latitude, hi.latitude); + let bottom = cmp::min(lo.latitude, hi.latitude); + + point.longitude >= left + && point.longitude <= right + && point.latitude >= bottom + && point.latitude <= top +} + +/// Calculates the distance between two points using the "haversine" formula. +/// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html. +fn calc_distance(p1: &Point, p2: &Point) -> i32 { + const CORD_FACTOR: f64 = 1e7; + const R: f64 = 6371000.0; // meters + + let lat1 = p1.latitude as f64 / CORD_FACTOR; + let lat2 = p2.latitude as f64 / CORD_FACTOR; + let lng1 = p1.longitude as f64 / CORD_FACTOR; + let lng2 = p2.longitude as f64 / CORD_FACTOR; + + let lat_rad1 = lat1.to_radians(); + let lat_rad2 = lat2.to_radians(); + + let delta_lat = (lat2 - lat1).to_radians(); + let delta_lng = (lng2 - lng1).to_radians(); + + let a = (delta_lat / 2f64).sin() * (delta_lat / 2f64).sin() + + (lat_rad1).cos() * (lat_rad2).cos() * (delta_lng / 2f64).sin() * (delta_lng / 2f64).sin(); + + let c = 2f64 * a.sqrt().atan2((1f64 - a).sqrt()); + + (R * c) as i32 +} diff --git a/tonic-macros/src/service.rs b/tonic-macros/src/service.rs index 3981852..c74f535 100644 --- a/tonic-macros/src/service.rs +++ b/tonic-macros/src/service.rs @@ -148,7 +148,12 @@ fn generate_methods(service: &ServiceDef) -> TokenStream { &service.proto_path, ), - _ => unimplemented!("method type"), + (true, true) => generate_streaming( + method, + ident.clone(), + service.name.clone(), + &service.proto_path, + ), }; let method = quote! { @@ -228,7 +233,10 @@ fn generate_server_streaming( fn call(&mut self, request: tonic::Request<#request>) -> Self::Future { let inner = self.0.clone(); let fut = async move { - inner.#method_ident(request).await + inner.#method_ident(request) + .await + .map(|r| + r.map(|s| Box::pin(s) as Pin> + Send>>)) }; Box::pin(fut) @@ -260,10 +268,8 @@ fn generate_client_streaming( let response: Path = syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap(); - // TODO: parse response stream type, if it is a concrete type then use that - // as the ResponseStream type, if it is a impl Trait then we need to box. quote! { - struct #service_ident ;//(pub std::sync::Arc<#service_impl>); + struct #service_ident(pub std::sync::Arc<#service_impl>); impl tonic::server::ClientStreamingService for #service_ident where S: Stream> + Unpin + Send + 'static { @@ -271,13 +277,12 @@ fn generate_client_streaming( type Future = BoxFuture, tonic::Status>; fn call(&mut self, request: tonic::Request) -> Self::Future { - // let inner = self.0.clone(); - // let fut = async move { - // inner.#method_ident(request).await + let inner = self.0.clone(); + let fut = async move { + inner.#method_ident(request).await - // }; - // Box::pin(fut) - unimplemented!() + }; + Box::pin(fut) } } @@ -293,3 +298,51 @@ fn generate_client_streaming( Box::pin(fut) } } + +fn generate_streaming( + method: &Method, + method_ident: Ident, + service_impl: Path, + proto_path: &str, +) -> TokenStream { + let service_ident = Ident::new(&method.proto_name, Span::call_site()); + + let request: Path = syn::parse_str(&format!("{}::{}", proto_path, method.input_type)).unwrap(); + let response: Path = + syn::parse_str(&format!("{}::{}", proto_path, method.output_type)).unwrap(); + + // TODO: parse response stream type, if it is a concrete type then use that + // as the ResponseStream type, if it is a impl Trait then we need to box. + quote! { + struct #service_ident(pub std::sync::Arc<#service_impl>); + + impl tonic::server::StreamingService for #service_ident + where S: Stream> + Unpin + Send + 'static { + type Response = #response; + type ResponseStream = Pin> + Send>>; + type Future = BoxFuture, tonic::Status>; + + fn call(&mut self, request: tonic::Request) -> Self::Future { + let inner = self.0.clone(); + let fut = async move { + inner.#method_ident(request).await + .map(|r| + r.map(|s| Box::pin(s) as Pin> + Send>>)) + + }; + Box::pin(fut) + } + } + + let inner = self.inner.clone(); + let fut = async move { + let method = #service_ident(inner); + let codec = tonic::codec::ProstCodec::new(); + let mut grpc = tonic::server::Grpc::new(codec); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + + Box::pin(fut) + } +} diff --git a/tonic/src/codec.rs b/tonic/src/codec.rs index 52d5b6f..8ca3d83 100644 --- a/tonic/src/codec.rs +++ b/tonic/src/codec.rs @@ -6,6 +6,7 @@ use futures_util::{future, StreamExt}; use http_body::Body; use prost::Message; use std::marker::PhantomData; +use std::pin::Pin; use tokio_codec::{Decoder, Encoder}; use tracing::{debug, trace}; @@ -22,6 +23,26 @@ pub trait Codec { fn decoder(&mut self) -> Self::Decoder; } +pub struct Streaming { + inner: Pin> + Send + 'static>>, +} + +impl Streaming { + pub fn new(inner: impl Stream> + Send + 'static) -> Self { + let inner = Box::pin(inner); + Self { inner } + } +} + +use std::task::{Context, Poll}; +impl Stream for Streaming { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_next(cx) + } +} + pub fn encode(mut encoder: T, mut source: U) -> impl TryStream where T: Encoder, diff --git a/tonic/src/server/grpc.rs b/tonic/src/server/grpc.rs index b108119..f73f6f1 100644 --- a/tonic/src/server/grpc.rs +++ b/tonic/src/server/grpc.rs @@ -1,11 +1,11 @@ use crate::{ body::{BoxAsyncBody, BytesBuf}, - codec::{decode, encode, Codec}, + codec::{decode, encode, Codec, Streaming}, server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService}, Code, Request, Response, Status, }; use futures_core::{Stream, TryStream}; -use futures_util::{future, stream, StreamExt, TryStreamExt}; +use futures_util::{future, stream, TryStreamExt}; use http_body::Body; use std::pin::Pin; @@ -83,18 +83,19 @@ where self.map_response(response).map(BoxAsyncBody::new_try) } +//BoxStream, pub async fn client_streaming( &mut self, mut service: S, req: http::Request, ) -> http::Response where - S: ClientStreamingService, Response = T::Encode>, - T::Decode: Send, + S: ClientStreamingService, Response = T::Encode>, + T::Decode: Send + 'static, T::Decoder: Send + 'static, B: Body + Send + 'static, - B::Data: Send, - B::Error: Into + Send, + B::Data: Send + 'static, + B::Error: Into + Send + 'static, { let request = self.map_request_streaming(req); let response = service @@ -110,7 +111,7 @@ where req: http::Request, ) -> http::Response where - S: StreamingService, Response = T::Encode> + Send, + S: StreamingService, Response = T::Encode> + Send, S::ResponseStream: Send + 'static, B: Body + Send + 'static, B::Data: Send, @@ -146,7 +147,7 @@ where fn map_request_streaming( &mut self, request: http::Request, - ) -> Request> + ) -> Request> where B: Body + Send + 'static, B::Data: Send, @@ -154,7 +155,7 @@ where { Request::from_http( request.map(|b| { - decode(self.codec.decoder(), b).into_stream().boxed() as BoxStream + Streaming::new(decode(self.codec.decoder(), b).into_stream()) }), ) } diff --git a/tonic/tests/h2.rs b/tonic/tests/h2.rs new file mode 100644 index 0000000..1894cd2 --- /dev/null +++ b/tonic/tests/h2.rs @@ -0,0 +1,139 @@ +#![feature(async_await, type_alias_impl_trait)] + +use futures_util::future; +use futures_core::Stream; +use std::pin::Pin; +use std::future::Future; +use std::task::{Context, Poll}; +use tokio::net::TcpListener; +use tonic::{ + body, + server::{Grpc, UnaryService, ClientStreamingService}, + Request, Response, Status, +}; +use tower_h2::{RecvBody, Server}; +use tower_service::Service; + +#[derive(Clone, PartialEq, prost::Message)] +pub struct HelloRequest { + #[prost(string, tag = "1")] + pub name: std::string::String, +} +/// The response message containing the greetings +#[derive(Clone, PartialEq, prost::Message)] +pub struct HelloReply { + #[prost(string, tag = "1")] + pub message: std::string::String, +} + +struct SayHello; + +impl UnaryService for SayHello { + type Response = HelloReply; + type Future = impl Future, Status>>; + + fn call(&mut self, request: Request) -> Self::Future { + async move { + println!("REQUEST = {:?}", request); + + let reply = HelloReply { + message: "Zomg, it works!".to_string(), + }; + + Ok(Response::new(reply)) + } + } +} + +struct SayHelloStream; + +impl ClientStreamingService for SayHelloStream +where S: Stream> + Unpin + Send + 'static { + type Response = HelloReply; + // type Future = impl Future, Status>>; + type Future = Pin, Status>> + Send + 'static>>; + + fn call(&mut self, _req: Request) -> Self::Future { + let fut = async move { + Ok(Response::new(HelloReply { message: "hello".into()})) + }; + Box::pin(fut) + } +} + +#[tokio::test] +async fn main() { + let addr = "[::1]:50051".parse().unwrap(); + let mut bind = TcpListener::bind(&addr).unwrap(); + + let mut server = Server::new(MakeSvc, Default::default()); + + while let Ok((sock, _addr)) = bind.accept().await { + if let Err(e) = sock.set_nodelay(true) { + panic!("error: {}", e); + } + + if let Err(e) = server.serve(sock).await { + println!("H2 ERROR: {}", e); + } + } +} + +#[derive(Debug)] +pub struct Svc; + +impl Service> for Svc { + type Response = http::Response; + type Error = tonic::error::Never; + // type Future = impl Future>; + type Future = Pin>>>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Ok(()).into() + } + + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/greeter.Helloworld/SayHello" => { + let fut = async move { + let codec = tonic::codec::ProstCodec::new(); + let mut grpc = Grpc::new(codec); + let response = grpc.unary(SayHello, req).await; + Ok(response) + }; + + Box::pin(fut) + } + + "/greeter.Helloworld/SayHelloStreaming" => { + let fut = async move { + let codec = tonic::codec::ProstCodec::new(); + let mut grpc = Grpc::new(codec); + let response = grpc.client_streaming(SayHelloStream, req).await; + Ok(response) + }; + + Box::pin(fut) + } + + _ => unimplemented!() + } + + } +} + +pub struct MakeSvc; + +impl Service<()> for MakeSvc { + type Response = Svc; + type Error = std::io::Error; + type Future = future::Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Ok(()).into() + } + + fn call(&mut self, _: ()) -> Self::Future { + future::ok(Svc) + } +}