xref: /tonic/examples/src/h2c/client.rs (revision 60b131d2)
1 use hello_world::greeter_client::GreeterClient;
2 use hello_world::HelloRequest;
3 use http::Uri;
4 use hyper_util::client::legacy::Client;
5 use hyper_util::rt::TokioExecutor;
6 
7 pub mod hello_world {
8     tonic::include_proto!("helloworld");
9 }
10 
11 #[tokio::main]
main() -> Result<(), Box<dyn std::error::Error>>12 async fn main() -> Result<(), Box<dyn std::error::Error>> {
13     let origin = Uri::from_static("http://[::1]:50051");
14     let h2c_client = h2c::H2cChannel {
15         client: Client::builder(TokioExecutor::new()).build_http(),
16     };
17 
18     let mut client = GreeterClient::with_origin(h2c_client, origin);
19 
20     let request = tonic::Request::new(HelloRequest {
21         name: "Tonic".into(),
22     });
23 
24     let response = client.say_hello(request).await?;
25 
26     println!("RESPONSE={:?}", response);
27 
28     Ok(())
29 }
30 
31 mod h2c {
32     use std::{
33         pin::Pin,
34         task::{Context, Poll},
35     };
36 
37     use hyper::body::Incoming;
38     use hyper_util::{
39         client::legacy::{connect::HttpConnector, Client},
40         rt::TokioExecutor,
41     };
42     use tonic::body::Body;
43     use tower::Service;
44 
45     pub struct H2cChannel {
46         pub client: Client<HttpConnector, Body>,
47     }
48 
49     impl Service<http::Request<Body>> for H2cChannel {
50         type Response = http::Response<Incoming>;
51         type Error = hyper::Error;
52         type Future =
53             Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
54 
poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>>55         fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
56             Poll::Ready(Ok(()))
57         }
58 
call(&mut self, request: http::Request<Body>) -> Self::Future59         fn call(&mut self, request: http::Request<Body>) -> Self::Future {
60             let client = self.client.clone();
61 
62             Box::pin(async move {
63                 let origin = request.uri();
64 
65                 let h2c_req = hyper::Request::builder()
66                     .uri(origin)
67                     .header(http::header::UPGRADE, "h2c")
68                     .body(Body::default())
69                     .unwrap();
70 
71                 let res = client.request(h2c_req).await.unwrap();
72 
73                 if res.status() != http::StatusCode::SWITCHING_PROTOCOLS {
74                     panic!("Our server didn't upgrade: {}", res.status());
75                 }
76 
77                 let upgraded_io = hyper::upgrade::on(res).await.unwrap();
78 
79                 // In an ideal world you would somehow cache this connection
80                 let (mut h2_client, conn) =
81                     hyper::client::conn::http2::Builder::new(TokioExecutor::new())
82                         .handshake(upgraded_io)
83                         .await
84                         .unwrap();
85                 tokio::spawn(conn);
86 
87                 h2_client.send_request(request).await
88             })
89         }
90     }
91 }
92