xref: /tonic/examples/src/grpc-web/client.rs (revision ff6a690c)
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 mut buf = BytesMut::with_capacity(1024);
50 
51     // first skip past the header
52     // cannot write it yet since we don't know the size of the
53     // encoded message
54     buf.reserve(GRPC_HEADER_SIZE);
55     unsafe {
56         buf.advance_mut(GRPC_HEADER_SIZE);
57     }
58 
59     // write the message
60     msg.encode(&mut buf).unwrap();
61 
62     // now we know the size of encoded message and can write the
63     // header
64     let len = buf.len() - GRPC_HEADER_SIZE;
65     {
66         let mut buf = &mut buf[..GRPC_HEADER_SIZE];
67 
68         // compression flag, 0 means "no compression"
69         buf.put_u8(0);
70 
71         buf.put_u32(len as u32);
72     }
73 
74     buf.split_to(len + GRPC_HEADER_SIZE).freeze()
75 }
76 
77 async fn decode_body<T>(body: hyper::Body) -> T
78 where
79     T: Default + prost::Message,
80 {
81     let mut body = hyper::body::to_bytes(body).await.unwrap();
82 
83     // ignore the compression flag
84     body.advance(1);
85 
86     let len = body.get_u32();
87     let msg = T::decode(&mut body.split_to(len as usize)).unwrap();
88 
89     msg
90 }
91