1 use std::fmt;
2 use std::pin::Pin;
3 use std::task::{ready, Context, Poll};
4
5 use base64::Engine as _;
6 use bytes::{Buf, BufMut, Bytes, BytesMut};
7 use http::{header, HeaderMap, HeaderName, HeaderValue};
8 use http_body::{Body, Frame, SizeHint};
9 use pin_project::pin_project;
10 use tokio_stream::Stream;
11 use tonic::Status;
12
13 use self::content_types::*;
14
15 // A grpc header is u8 (flag) + u32 (msg len)
16 const GRPC_HEADER_SIZE: usize = 1 + 4;
17
18 pub(crate) mod content_types {
19 use http::{header::CONTENT_TYPE, HeaderMap};
20
21 pub(crate) const GRPC_WEB: &str = "application/grpc-web";
22 pub(crate) const GRPC_WEB_PROTO: &str = "application/grpc-web+proto";
23 pub(crate) const GRPC_WEB_TEXT: &str = "application/grpc-web-text";
24 pub(crate) const GRPC_WEB_TEXT_PROTO: &str = "application/grpc-web-text+proto";
25
is_grpc_web(headers: &HeaderMap) -> bool26 pub(crate) fn is_grpc_web(headers: &HeaderMap) -> bool {
27 matches!(
28 content_type(headers),
29 Some(GRPC_WEB) | Some(GRPC_WEB_PROTO) | Some(GRPC_WEB_TEXT) | Some(GRPC_WEB_TEXT_PROTO)
30 )
31 }
32
content_type(headers: &HeaderMap) -> Option<&str>33 fn content_type(headers: &HeaderMap) -> Option<&str> {
34 headers.get(CONTENT_TYPE).and_then(|val| val.to_str().ok())
35 }
36 }
37
38 const BUFFER_SIZE: usize = 8 * 1024;
39
40 const FRAME_HEADER_SIZE: usize = 5;
41
42 // 8th (MSB) bit of the 1st gRPC frame byte
43 // denotes an uncompressed trailer (as part of the body)
44 const GRPC_WEB_TRAILERS_BIT: u8 = 0b10000000;
45
46 #[derive(Copy, Clone, PartialEq, Debug)]
47 enum Direction {
48 Decode,
49 Encode,
50 Empty,
51 }
52
53 #[derive(Copy, Clone, PartialEq, Debug)]
54 pub(crate) enum Encoding {
55 Base64,
56 None,
57 }
58
59 /// HttpBody adapter for the grpc web based services.
60 #[derive(Debug)]
61 #[pin_project]
62 pub struct GrpcWebCall<B> {
63 #[pin]
64 inner: B,
65 buf: BytesMut,
66 decoded: BytesMut,
67 direction: Direction,
68 encoding: Encoding,
69 client: bool,
70 trailers: Option<HeaderMap>,
71 }
72
73 impl<B: Default> Default for GrpcWebCall<B> {
default() -> Self74 fn default() -> Self {
75 Self {
76 inner: Default::default(),
77 buf: Default::default(),
78 decoded: Default::default(),
79 direction: Direction::Empty,
80 encoding: Encoding::None,
81 client: Default::default(),
82 trailers: Default::default(),
83 }
84 }
85 }
86
87 impl<B> GrpcWebCall<B> {
request(inner: B, encoding: Encoding) -> Self88 pub(crate) fn request(inner: B, encoding: Encoding) -> Self {
89 Self::new(inner, Direction::Decode, encoding)
90 }
91
response(inner: B, encoding: Encoding) -> Self92 pub(crate) fn response(inner: B, encoding: Encoding) -> Self {
93 Self::new(inner, Direction::Encode, encoding)
94 }
95
client_request(inner: B) -> Self96 pub(crate) fn client_request(inner: B) -> Self {
97 Self::new_client(inner, Direction::Encode, Encoding::None)
98 }
99
client_response(inner: B) -> Self100 pub(crate) fn client_response(inner: B) -> Self {
101 Self::new_client(inner, Direction::Decode, Encoding::None)
102 }
103
new_client(inner: B, direction: Direction, encoding: Encoding) -> Self104 fn new_client(inner: B, direction: Direction, encoding: Encoding) -> Self {
105 GrpcWebCall {
106 inner,
107 buf: BytesMut::with_capacity(match (direction, encoding) {
108 (Direction::Encode, Encoding::Base64) => BUFFER_SIZE,
109 _ => 0,
110 }),
111 decoded: BytesMut::with_capacity(match direction {
112 Direction::Decode => BUFFER_SIZE,
113 _ => 0,
114 }),
115 direction,
116 encoding,
117 client: true,
118 trailers: None,
119 }
120 }
121
new(inner: B, direction: Direction, encoding: Encoding) -> Self122 fn new(inner: B, direction: Direction, encoding: Encoding) -> Self {
123 GrpcWebCall {
124 inner,
125 buf: BytesMut::with_capacity(match (direction, encoding) {
126 (Direction::Encode, Encoding::Base64) => BUFFER_SIZE,
127 _ => 0,
128 }),
129 decoded: BytesMut::with_capacity(0),
130 direction,
131 encoding,
132 client: false,
133 trailers: None,
134 }
135 }
136
137 // This is to avoid passing a slice of bytes with a length that the base64
138 // decoder would consider invalid.
139 #[inline]
max_decodable(&self) -> usize140 fn max_decodable(&self) -> usize {
141 (self.buf.len() / 4) * 4
142 }
143
decode_chunk(mut self: Pin<&mut Self>) -> Result<Option<Bytes>, Status>144 fn decode_chunk(mut self: Pin<&mut Self>) -> Result<Option<Bytes>, Status> {
145 // not enough bytes to decode
146 if self.buf.is_empty() || self.buf.len() < 4 {
147 return Ok(None);
148 }
149
150 // Split `buf` at the largest index that is multiple of 4. Decode the
151 // returned `Bytes`, keeping the rest for the next attempt to decode.
152 let index = self.max_decodable();
153
154 crate::util::base64::STANDARD
155 .decode(self.as_mut().project().buf.split_to(index))
156 .map(|decoded| Some(Bytes::from(decoded)))
157 .map_err(internal_error)
158 }
159 }
160
161 impl<B> GrpcWebCall<B>
162 where
163 B: Body,
164 B::Data: Buf,
165 B::Error: fmt::Display,
166 {
167 // Poll body for data, decoding (e.g. via Base64 if necessary) and returning frames
168 // to the caller. If the caller is a client, it should look for trailers before
169 // returning these frames.
poll_decode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Result<Frame<Bytes>, Status>>>170 fn poll_decode(
171 mut self: Pin<&mut Self>,
172 cx: &mut Context<'_>,
173 ) -> Poll<Option<Result<Frame<Bytes>, Status>>> {
174 match self.encoding {
175 Encoding::Base64 => loop {
176 if let Some(bytes) = self.as_mut().decode_chunk()? {
177 return Poll::Ready(Some(Ok(Frame::data(bytes))));
178 }
179
180 let this = self.as_mut().project();
181
182 match ready!(this.inner.poll_frame(cx)) {
183 Some(Ok(frame)) if frame.is_data() => this
184 .buf
185 .put(frame.into_data().unwrap_or_else(|_| unreachable!())),
186 Some(Ok(frame)) if frame.is_trailers() => {
187 return Poll::Ready(Some(Err(internal_error(
188 "malformed base64 request has unencoded trailers",
189 ))))
190 }
191 Some(Ok(_)) => {
192 return Poll::Ready(Some(Err(internal_error("unexpected frame type"))))
193 }
194 Some(Err(e)) => return Poll::Ready(Some(Err(internal_error(e)))),
195 None => {
196 return if this.buf.has_remaining() {
197 Poll::Ready(Some(Err(internal_error("malformed base64 request"))))
198 } else if let Some(trailers) = this.trailers.take() {
199 Poll::Ready(Some(Ok(Frame::trailers(trailers))))
200 } else {
201 Poll::Ready(None)
202 }
203 }
204 }
205 },
206
207 Encoding::None => self
208 .project()
209 .inner
210 .poll_frame(cx)
211 .map_ok(|f| f.map_data(|mut d| d.copy_to_bytes(d.remaining())))
212 .map_err(internal_error),
213 }
214 }
215
poll_encode( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Result<Frame<Bytes>, Status>>>216 fn poll_encode(
217 mut self: Pin<&mut Self>,
218 cx: &mut Context<'_>,
219 ) -> Poll<Option<Result<Frame<Bytes>, Status>>> {
220 let this = self.as_mut().project();
221
222 match ready!(this.inner.poll_frame(cx)) {
223 Some(Ok(frame)) if frame.is_data() => {
224 let mut data = frame.into_data().unwrap_or_else(|_| unreachable!());
225 let mut res = data.copy_to_bytes(data.remaining());
226
227 if *this.encoding == Encoding::Base64 {
228 res = crate::util::base64::STANDARD.encode(res).into();
229 }
230
231 Poll::Ready(Some(Ok(Frame::data(res))))
232 }
233 Some(Ok(frame)) if frame.is_trailers() => {
234 let trailers = frame.into_trailers().unwrap_or_else(|_| unreachable!());
235 let mut res = make_trailers_frame(trailers);
236
237 if *this.encoding == Encoding::Base64 {
238 res = crate::util::base64::STANDARD.encode(res).into();
239 }
240
241 Poll::Ready(Some(Ok(Frame::data(res))))
242 }
243 Some(Ok(_)) => Poll::Ready(Some(Err(internal_error("unexpected frame type")))),
244 Some(Err(e)) => Poll::Ready(Some(Err(internal_error(e)))),
245 None => Poll::Ready(None),
246 }
247 }
248 }
249
250 impl<B> Body for GrpcWebCall<B>
251 where
252 B: Body,
253 B::Error: fmt::Display,
254 {
255 type Data = Bytes;
256 type Error = Status;
257
poll_frame( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>>258 fn poll_frame(
259 mut self: Pin<&mut Self>,
260 cx: &mut Context<'_>,
261 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
262 if self.client && self.direction == Direction::Decode {
263 let mut me = self.as_mut();
264
265 loop {
266 match ready!(me.as_mut().poll_decode(cx)) {
267 Some(Ok(incoming_buf)) if incoming_buf.is_data() => {
268 me.as_mut()
269 .project()
270 .decoded
271 .put(incoming_buf.into_data().unwrap());
272 }
273 Some(Ok(incoming_buf)) if incoming_buf.is_trailers() => {
274 let trailers = incoming_buf.into_trailers().unwrap();
275 match me.as_mut().project().trailers {
276 Some(current_trailers) => {
277 current_trailers.extend(trailers);
278 }
279 None => {
280 me.as_mut().project().trailers.replace(trailers);
281 }
282 }
283 continue;
284 }
285 Some(Ok(_)) => unreachable!("unexpected frame type"),
286 None => {} // No more data to decode, time to look for trailers
287 Some(Err(e)) => return Poll::Ready(Some(Err(e))),
288 };
289
290 // Hold the incoming, decoded data until we have a full message
291 // or trailers to return.
292 let buf = me.as_mut().project().decoded;
293
294 return match find_trailers(&buf[..])? {
295 FindTrailers::Trailer(len) => {
296 // Extract up to len of where the trailers are at
297 let msg_buf = buf.copy_to_bytes(len);
298 match decode_trailers_frame(buf.split().freeze()) {
299 Ok(Some(trailers)) => {
300 me.as_mut().project().trailers.replace(trailers);
301 }
302 Err(e) => return Poll::Ready(Some(Err(e))),
303 _ => {}
304 }
305
306 if msg_buf.has_remaining() {
307 Poll::Ready(Some(Ok(Frame::data(msg_buf))))
308 } else if let Some(trailers) = me.as_mut().project().trailers.take() {
309 Poll::Ready(Some(Ok(Frame::trailers(trailers))))
310 } else {
311 Poll::Ready(None)
312 }
313 }
314 FindTrailers::IncompleteBuf => continue,
315 FindTrailers::Done(len) => Poll::Ready(match len {
316 0 => None,
317 _ => Some(Ok(Frame::data(buf.split_to(len).freeze()))),
318 }),
319 };
320 }
321 }
322
323 match self.direction {
324 Direction::Decode => self.poll_decode(cx),
325 Direction::Encode => self.poll_encode(cx),
326 Direction::Empty => Poll::Ready(None),
327 }
328 }
329
is_end_stream(&self) -> bool330 fn is_end_stream(&self) -> bool {
331 self.inner.is_end_stream()
332 }
333
size_hint(&self) -> SizeHint334 fn size_hint(&self) -> SizeHint {
335 self.inner.size_hint()
336 }
337 }
338
339 impl<B> Stream for GrpcWebCall<B>
340 where
341 B: Body,
342 B::Error: fmt::Display,
343 {
344 type Item = Result<Frame<Bytes>, Status>;
345
poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>346 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
347 self.poll_frame(cx)
348 }
349 }
350
351 impl Encoding {
from_content_type(headers: &HeaderMap) -> Encoding352 pub(crate) fn from_content_type(headers: &HeaderMap) -> Encoding {
353 Self::from_header(headers.get(header::CONTENT_TYPE))
354 }
355
from_accept(headers: &HeaderMap) -> Encoding356 pub(crate) fn from_accept(headers: &HeaderMap) -> Encoding {
357 Self::from_header(headers.get(header::ACCEPT))
358 }
359
to_content_type(self) -> &'static str360 pub(crate) fn to_content_type(self) -> &'static str {
361 match self {
362 Encoding::Base64 => GRPC_WEB_TEXT_PROTO,
363 Encoding::None => GRPC_WEB_PROTO,
364 }
365 }
366
from_header(value: Option<&HeaderValue>) -> Encoding367 fn from_header(value: Option<&HeaderValue>) -> Encoding {
368 match value.and_then(|val| val.to_str().ok()) {
369 Some(GRPC_WEB_TEXT_PROTO) | Some(GRPC_WEB_TEXT) => Encoding::Base64,
370 _ => Encoding::None,
371 }
372 }
373 }
374
internal_error(e: impl std::fmt::Display) -> Status375 fn internal_error(e: impl std::fmt::Display) -> Status {
376 Status::internal(format!("tonic-web: {}", e))
377 }
378
379 // Key-value pairs encoded as a HTTP/1 headers block (without the terminating newline)
encode_trailers(trailers: HeaderMap) -> Vec<u8>380 fn encode_trailers(trailers: HeaderMap) -> Vec<u8> {
381 trailers.iter().fold(Vec::new(), |mut acc, (key, value)| {
382 acc.put_slice(key.as_ref());
383 acc.push(b':');
384 acc.put_slice(value.as_bytes());
385 acc.put_slice(b"\r\n");
386 acc
387 })
388 }
389
decode_trailers_frame(mut buf: Bytes) -> Result<Option<HeaderMap>, Status>390 fn decode_trailers_frame(mut buf: Bytes) -> Result<Option<HeaderMap>, Status> {
391 if buf.remaining() < GRPC_HEADER_SIZE {
392 return Ok(None);
393 }
394
395 buf.get_u8();
396 buf.get_u32();
397
398 let mut map = HeaderMap::new();
399 let mut temp_buf = buf.clone();
400
401 let mut trailers = Vec::new();
402 let mut cursor_pos = 0;
403
404 for (i, b) in buf.iter().enumerate() {
405 // if we are at a trailer delimiter (\r\n)
406 if b == &b'\r' && buf.get(i + 1) == Some(&b'\n') {
407 // read the bytes of the trailer passed so far
408 let trailer = temp_buf.copy_to_bytes(i - cursor_pos);
409 // increment cursor beyond the delimiter
410 cursor_pos = i + 2;
411 trailers.push(trailer);
412 if temp_buf.has_remaining() {
413 // advance buf beyond the delimiters
414 temp_buf.get_u8();
415 temp_buf.get_u8();
416 }
417 }
418 }
419
420 for trailer in trailers {
421 let mut s = trailer.split(|b| b == &b':');
422 let key = s
423 .next()
424 .ok_or_else(|| Status::internal("trailers couldn't parse key"))?;
425 let value = s
426 .next()
427 .ok_or_else(|| Status::internal("trailers couldn't parse value"))?;
428
429 let value = value
430 .split(|b| b == &b'\r')
431 .next()
432 .ok_or_else(|| Status::internal("trailers was not escaped"))?
433 .strip_prefix(b" ")
434 .unwrap_or(value);
435
436 let header_key = HeaderName::try_from(key)
437 .map_err(|e| Status::internal(format!("Unable to parse HeaderName: {}", e)))?;
438 let header_value = HeaderValue::try_from(value)
439 .map_err(|e| Status::internal(format!("Unable to parse HeaderValue: {}", e)))?;
440 map.insert(header_key, header_value);
441 }
442
443 Ok(Some(map))
444 }
445
make_trailers_frame(trailers: HeaderMap) -> Bytes446 fn make_trailers_frame(trailers: HeaderMap) -> Bytes {
447 let trailers = encode_trailers(trailers);
448 let len = trailers.len();
449 assert!(len <= u32::MAX as usize);
450
451 let mut frame = BytesMut::with_capacity(len + FRAME_HEADER_SIZE);
452 frame.put_u8(GRPC_WEB_TRAILERS_BIT);
453 frame.put_u32(len as u32);
454 frame.put_slice(&trailers);
455
456 frame.freeze()
457 }
458
459 /// Search some buffer for grpc-web trailers headers and return
460 /// its location in the original buf. If `None` is returned we did
461 /// not find a trailers in this buffer either because its incomplete
462 /// or the buffer just contained grpc message frames.
find_trailers(buf: &[u8]) -> Result<FindTrailers, Status>463 fn find_trailers(buf: &[u8]) -> Result<FindTrailers, Status> {
464 let mut len = 0;
465 let mut temp_buf = buf;
466
467 loop {
468 // To check each frame, there must be at least GRPC_HEADER_SIZE
469 // amount of bytes available otherwise the buffer is incomplete.
470 if temp_buf.is_empty() || temp_buf.len() < GRPC_HEADER_SIZE {
471 return Ok(FindTrailers::Done(len));
472 }
473
474 let header = temp_buf.get_u8();
475
476 if header == GRPC_WEB_TRAILERS_BIT {
477 return Ok(FindTrailers::Trailer(len));
478 }
479
480 if !(header == 0 || header == 1) {
481 return Err(Status::internal(format!(
482 "Invalid header bit {} expected 0 or 1",
483 header
484 )));
485 }
486
487 let msg_len = temp_buf.get_u32();
488
489 len += msg_len as usize + 4 + 1;
490
491 // If the msg len of a non-grpc-web trailer frame is larger than
492 // the overall buffer we know within that buffer there are no trailers.
493 if len > buf.len() {
494 return Ok(FindTrailers::IncompleteBuf);
495 }
496
497 temp_buf = &buf[len..];
498 }
499 }
500
501 #[derive(Debug, PartialEq, Eq)]
502 enum FindTrailers {
503 Trailer(usize),
504 IncompleteBuf,
505 Done(usize),
506 }
507
508 #[cfg(test)]
509 mod tests {
510 use tonic::Code;
511
512 use super::*;
513
514 #[test]
encoding_constructors()515 fn encoding_constructors() {
516 let cases = &[
517 (GRPC_WEB, Encoding::None),
518 (GRPC_WEB_PROTO, Encoding::None),
519 (GRPC_WEB_TEXT, Encoding::Base64),
520 (GRPC_WEB_TEXT_PROTO, Encoding::Base64),
521 ("foo", Encoding::None),
522 ];
523
524 let mut headers = HeaderMap::new();
525
526 for case in cases {
527 headers.insert(header::CONTENT_TYPE, case.0.parse().unwrap());
528 headers.insert(header::ACCEPT, case.0.parse().unwrap());
529
530 assert_eq!(Encoding::from_content_type(&headers), case.1, "{}", case.0);
531 assert_eq!(Encoding::from_accept(&headers), case.1, "{}", case.0);
532 }
533 }
534
535 #[test]
decode_trailers()536 fn decode_trailers() {
537 let mut headers = HeaderMap::new();
538 headers.insert(Status::GRPC_STATUS, 0.into());
539 headers.insert(
540 Status::GRPC_MESSAGE,
541 "this is a message".try_into().unwrap(),
542 );
543
544 let trailers = make_trailers_frame(headers.clone());
545
546 let map = decode_trailers_frame(trailers).unwrap().unwrap();
547
548 assert_eq!(headers, map);
549 }
550
551 #[test]
find_trailers_non_buffered()552 fn find_trailers_non_buffered() {
553 // Byte version of this:
554 // b"\x80\0\0\0\x0fgrpc-status:0\r\n"
555 let buf = [
556 128, 0, 0, 0, 15, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, 115, 58, 48, 13, 10,
557 ];
558
559 let out = find_trailers(&buf[..]).unwrap();
560
561 assert_eq!(out, FindTrailers::Trailer(0));
562 }
563
564 #[test]
find_trailers_buffered()565 fn find_trailers_buffered() {
566 // Byte version of this:
567 // b"\0\0\0\0L\n$975738af-1a17-4aea-b887-ed0bbced6093\x1a$da609e9b-f470-4cc0-a691-3fd6a005a436\x80\0\0\0\x0fgrpc-status:0\r\n"
568 let buf = [
569 0, 0, 0, 0, 76, 10, 36, 57, 55, 53, 55, 51, 56, 97, 102, 45, 49, 97, 49, 55, 45, 52,
570 97, 101, 97, 45, 98, 56, 56, 55, 45, 101, 100, 48, 98, 98, 99, 101, 100, 54, 48, 57,
571 51, 26, 36, 100, 97, 54, 48, 57, 101, 57, 98, 45, 102, 52, 55, 48, 45, 52, 99, 99, 48,
572 45, 97, 54, 57, 49, 45, 51, 102, 100, 54, 97, 48, 48, 53, 97, 52, 51, 54, 128, 0, 0, 0,
573 15, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, 115, 58, 48, 13, 10,
574 ];
575
576 let out = find_trailers(&buf[..]).unwrap();
577
578 assert_eq!(out, FindTrailers::Trailer(81));
579
580 let trailers = decode_trailers_frame(Bytes::copy_from_slice(&buf[81..]))
581 .unwrap()
582 .unwrap();
583 let status = trailers.get(Status::GRPC_STATUS).unwrap();
584 assert_eq!(status.to_str().unwrap(), "0")
585 }
586
587 #[test]
find_trailers_buffered_incomplete_message()588 fn find_trailers_buffered_incomplete_message() {
589 let buf = vec![
590 0, 0, 0, 9, 238, 10, 233, 19, 18, 230, 19, 10, 9, 10, 1, 120, 26, 4, 84, 69, 88, 84,
591 18, 60, 10, 58, 10, 56, 3, 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, 0, 116, 104, 105, 115, 32,
592 118, 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114, 105, 116, 116, 101, 110, 32,
593 118, 105, 97, 32, 119, 114, 105, 116, 101, 32, 100, 101, 108, 101, 103, 97, 116, 105,
594 111, 110, 33, 18, 62, 10, 60, 10, 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 116, 104,
595 105, 115, 32, 118, 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114, 105, 116, 116,
596 101, 110, 32, 98, 121, 32, 97, 110, 32, 101, 109, 98, 101, 100, 100, 101, 100, 32, 114,
597 101, 112, 108, 105, 99, 97, 33, 18, 62, 10, 60, 10, 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0,
598 0, 0, 116, 104, 105, 115, 32, 118, 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114,
599 105, 116, 116, 101, 110, 32, 98, 121, 32, 97, 110, 32, 101, 109, 98, 101, 100, 100,
600 101, 100, 32, 114, 101, 112, 108, 105, 99, 97, 33, 18, 62, 10, 60, 10, 58, 3, 0, 0, 0,
601 46, 0, 0, 0, 0, 0, 0, 0, 116, 104, 105, 115, 32, 118, 97, 108, 117, 101, 32, 119, 97,
602 115, 32, 119, 114, 105, 116, 116, 101, 110, 32, 98, 121, 32, 97, 110, 32, 101, 109, 98,
603 101, 100, 100, 101, 100, 32, 114, 101, 112, 108, 105, 99, 97, 33, 18, 62, 10, 60, 10,
604 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 116, 104, 105, 115, 32, 118, 97, 108, 117,
605 101, 32, 119, 97, 115, 32, 119, 114, 105, 116, 116, 101, 110, 32, 98, 121, 32, 97, 110,
606 32, 101, 109, 98, 101, 100, 100, 101, 100, 32, 114, 101, 112, 108, 105, 99, 97, 33, 18,
607 62, 10, 60, 10, 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 116, 104, 105, 115, 32, 118,
608 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114, 105, 116, 116, 101, 110, 32, 98,
609 121, 32, 97, 110, 32, 101, 109, 98, 101, 100, 100, 101, 100, 32, 114, 101, 112, 108,
610 105, 99, 97, 33, 18, 62, 10, 60, 10, 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 116, 104,
611 105, 115, 32, 118, 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114, 105, 116, 116,
612 101, 110, 32, 98, 121, 32, 97, 110, 32, 101, 109, 98, 101, 100, 100, 101, 100, 32, 114,
613 101, 112, 108, 105, 99, 97, 33, 18, 62, 10, 60, 10, 58, 3, 0, 0, 0, 46, 0, 0, 0, 0, 0,
614 0, 0, 116, 104, 105, 115, 32, 118, 97, 108, 117, 101, 32, 119, 97, 115, 32, 119, 114,
615 105, 116, 116, 101, 110, 32, 98, 121, 32,
616 ];
617
618 let out = find_trailers(&buf[..]).unwrap();
619
620 assert_eq!(out, FindTrailers::IncompleteBuf);
621 }
622
623 #[test]
624 #[ignore]
find_trailers_buffered_incomplete_buf_bug()625 fn find_trailers_buffered_incomplete_buf_bug() {
626 let buf = std::fs::read("tests/incomplete-buf-bug.bin").unwrap();
627 let out = find_trailers(&buf[..]).unwrap_err();
628
629 assert_eq!(out.code(), Code::Internal);
630 }
631
632 #[test]
decode_multiple_trailers()633 fn decode_multiple_trailers() {
634 let buf = b"\x80\0\0\0\x0fgrpc-status:0\r\ngrpc-message:\r\na:1\r\nb:2\r\n";
635
636 let trailers = decode_trailers_frame(Bytes::copy_from_slice(&buf[..]))
637 .unwrap()
638 .unwrap();
639
640 let mut expected = HeaderMap::new();
641 expected.insert(Status::GRPC_STATUS, "0".parse().unwrap());
642 expected.insert(Status::GRPC_MESSAGE, "".parse().unwrap());
643 expected.insert("a", "1".parse().unwrap());
644 expected.insert("b", "2".parse().unwrap());
645
646 assert_eq!(trailers, expected);
647 }
648
649 #[test]
decode_trailers_with_space_after_colon()650 fn decode_trailers_with_space_after_colon() {
651 let buf = b"\x80\0\0\0\x0fgrpc-status: 0\r\ngrpc-message: \r\n";
652
653 let trailers = decode_trailers_frame(Bytes::copy_from_slice(&buf[..]))
654 .unwrap()
655 .unwrap();
656
657 let mut expected = HeaderMap::new();
658 expected.insert(Status::GRPC_STATUS, "0".parse().unwrap());
659 expected.insert(Status::GRPC_MESSAGE, "".parse().unwrap());
660
661 assert_eq!(trailers, expected);
662 }
663 }
664