1 use std::net::SocketAddr;
2
3 use base64::Engine as _;
4 use bytes::{Buf, BufMut, Bytes, BytesMut};
5 use http_body_util::{BodyExt as _, Full};
6 use hyper::body::Incoming;
7 use hyper::http::{header, StatusCode};
8 use hyper::{Method, Request, Uri};
9 use hyper_util::client::legacy::Client;
10 use hyper_util::rt::TokioExecutor;
11 use prost::Message;
12 use tokio::net::TcpListener;
13 use tokio_stream::wrappers::TcpListenerStream;
14 use tonic::body::Body;
15 use tonic::transport::Server;
16
17 use test_web::pb::{test_server::TestServer, Input, Output};
18 use test_web::Svc;
19 use tonic::Status;
20 use tonic_web::GrpcWebLayer;
21
22 #[tokio::test]
binary_request()23 async fn binary_request() {
24 let server_url = spawn().await;
25 let client = Client::builder(TokioExecutor::new()).build_http();
26
27 let req = build_request(server_url, "grpc-web", "grpc-web");
28 let res = client.request(req).await.unwrap();
29 let content_type = res.headers().get(header::CONTENT_TYPE).unwrap().clone();
30 let content_type = content_type.to_str().unwrap();
31
32 assert_eq!(res.status(), StatusCode::OK);
33 assert_eq!(content_type, "application/grpc-web+proto");
34
35 let (message, trailers) = decode_body(res.into_body(), content_type).await;
36 let expected = Output {
37 id: 1,
38 desc: "one".to_owned(),
39 };
40
41 assert_eq!(message, expected);
42 assert_eq!(&trailers[..], b"grpc-status:0\r\n");
43 }
44
45 #[tokio::test]
text_request()46 async fn text_request() {
47 let server_url = spawn().await;
48 let client = Client::builder(TokioExecutor::new()).build_http();
49
50 let req = build_request(server_url, "grpc-web-text", "grpc-web-text");
51 let res = client.request(req).await.unwrap();
52 let content_type = res.headers().get(header::CONTENT_TYPE).unwrap().clone();
53 let content_type = content_type.to_str().unwrap();
54
55 assert_eq!(res.status(), StatusCode::OK);
56 assert_eq!(content_type, "application/grpc-web-text+proto");
57
58 let (message, trailers) = decode_body(res.into_body(), content_type).await;
59 let expected = Output {
60 id: 1,
61 desc: "one".to_owned(),
62 };
63
64 assert_eq!(message, expected);
65 assert_eq!(&trailers[..], b"grpc-status:0\r\n");
66 }
67
spawn() -> String68 async fn spawn() -> String {
69 let addr = SocketAddr::from(([127, 0, 0, 1], 0));
70 let listener = TcpListener::bind(addr).await.expect("listener");
71 let url = format!("http://{}", listener.local_addr().unwrap());
72 let listener_stream = TcpListenerStream::new(listener);
73
74 drop(tokio::spawn(async move {
75 Server::builder()
76 .accept_http1(true)
77 .layer(GrpcWebLayer::new())
78 .add_service(TestServer::new(Svc))
79 .serve_with_incoming(listener_stream)
80 .await
81 .unwrap()
82 }));
83
84 url
85 }
86
encode_body() -> Bytes87 fn encode_body() -> Bytes {
88 let input = Input {
89 id: 1,
90 desc: "one".to_owned(),
91 };
92
93 let mut buf = BytesMut::with_capacity(1024);
94 buf.reserve(5);
95 unsafe {
96 buf.advance_mut(5);
97 }
98
99 input.encode(&mut buf).unwrap();
100
101 let len = buf.len() - 5;
102 {
103 let mut buf = &mut buf[..5];
104 buf.put_u8(0);
105 buf.put_u32(len as u32);
106 }
107
108 buf.split_to(len + 5).freeze()
109 }
110
build_request(base_uri: String, content_type: &str, accept: &str) -> Request<Body>111 fn build_request(base_uri: String, content_type: &str, accept: &str) -> Request<Body> {
112 use header::{ACCEPT, CONTENT_TYPE, ORIGIN};
113
114 let request_uri = format!("{}/{}/{}", base_uri, "test.Test", "UnaryCall")
115 .parse::<Uri>()
116 .unwrap();
117
118 let bytes = match content_type {
119 "grpc-web" => encode_body(),
120 "grpc-web-text" => test_web::util::base64::STANDARD
121 .encode(encode_body())
122 .into(),
123 _ => panic!("invalid content type {}", content_type),
124 };
125
126 Request::builder()
127 .method(Method::POST)
128 .header(CONTENT_TYPE, format!("application/{}", content_type))
129 .header(ORIGIN, "http://example.com")
130 .header(ACCEPT, format!("application/{}", accept))
131 .uri(request_uri)
132 .body(Body::new(
133 Full::new(bytes).map_err(|err| Status::internal(err.to_string())),
134 ))
135 .unwrap()
136 }
137
decode_body(body: Incoming, content_type: &str) -> (Output, Bytes)138 async fn decode_body(body: Incoming, content_type: &str) -> (Output, Bytes) {
139 let mut body = body.collect().await.unwrap().to_bytes();
140
141 if content_type == "application/grpc-web-text+proto" {
142 body = test_web::util::base64::STANDARD
143 .decode(body)
144 .unwrap()
145 .into()
146 }
147
148 body.advance(1);
149 let len = body.get_u32();
150 let msg = Output::decode(&mut body.split_to(len as usize)).expect("decode");
151 body.advance(5);
152
153 (msg, body)
154 }
155