xref: /tonic/examples/src/grpc-web/client.rs (revision 3f4d7432)
1 use bytes::{Buf, BufMut, Bytes, BytesMut};
2 use hello_world::{HelloReply, HelloRequest};
3 use http::header::{ACCEPT, CONTENT_TYPE};
4 
5 pub mod hello_world {
6     tonic::include_proto!("helloworld");
7 }
8 
9 #[tokio::main]
10 async fn main() -> Result<(), Box<dyn std::error::Error>> {
11     let msg = HelloRequest {
12         name: "Bob".to_string(),
13     };
14 
15     // a good old http/1.1 request
16     let request = http::Request::builder()
17         .version(http::Version::HTTP_11)
18         .method(http::Method::POST)
19         .uri("http://127.0.0.1:3000/helloworld.Greeter/SayHello")
20         .header(CONTENT_TYPE, "application/grpc-web")
21         .header(ACCEPT, "application/grpc-web")
22         .body(hyper::Body::from(encode_body(msg)))
23         .unwrap();
24 
25     let client = hyper::Client::new();
26 
27     let response = client.request(request).await.unwrap();
28 
29     assert_eq!(
30         response.headers().get(CONTENT_TYPE).unwrap(),
31         "application/grpc-web+proto"
32     );
33 
34     let body = response.into_body();
35     let reply = decode_body::<HelloReply>(body).await;
36 
37     println!("REPLY={:?}", reply);
38 
39     Ok(())
40 }
41 
42 // one byte for the compression flag plus four bytes for the length
43 const GRPC_HEADER_SIZE: usize = 5;
44 
45 fn encode_body<T>(msg: T) -> Bytes
46 where
47     T: prost::Message,
48 {
49     let msg_len = msg.encoded_len();
50     let mut buf = BytesMut::with_capacity(GRPC_HEADER_SIZE + msg_len);
51 
52     // compression flag, 0 means "no compression"
53     buf.put_u8(0);
54     buf.put_u32(msg_len as u32);
55 
56     msg.encode(&mut buf).unwrap();
57     buf.freeze()
58 }
59 
60 async fn decode_body<T>(body: hyper::Body) -> T
61 where
62     T: Default + prost::Message,
63 {
64     let mut body = hyper::body::to_bytes(body).await.unwrap();
65 
66     // ignore the compression flag
67     body.advance(1);
68 
69     let len = body.get_u32();
70     #[allow(clippy::let_and_return)]
71     let msg = T::decode(&mut body.split_to(len as usize)).unwrap();
72 
73     msg
74 }
75