1use std::{borrow::Cow, fmt::Write, mem};
2
3use base64::engine::Engine;
4use base64::engine::general_purpose::{STANDARD, STANDARD_NO_PAD};
5use ntex_bytes::{BytePages, Bytes, BytesMut};
6#[cfg(not(feature = "compression"))]
7use ntex_http::HeaderMap;
8use ntex_http::HeaderValue;
9use urly::quoting::{Component, unquote};
10
11use crate::GrpcStatus;
12
13#[cfg(feature = "compression")]
16pub(crate) use crate::Compression as Enc;
17
18#[cfg(not(feature = "compression"))]
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub(crate) enum Enc {}
21
22#[cfg(not(feature = "compression"))]
23#[allow(clippy::unused_async)]
24impl Enc {
25 pub(crate) async fn compress(
26 self,
27 _: &mut BytePages,
28 ) -> Result<bool, (GrpcStatus, HeaderValue)> {
29 match self {}
30 }
31
32 pub(crate) fn header(self) -> HeaderValue {
33 match self {}
34 }
35
36 pub(crate) fn of_response(_: &HeaderMap) -> Option<Self> {
37 None
38 }
39}
40
41pub(crate) enum Data {
42 Chunk(Bytes),
43 MutChunk(BytesMut),
44 Empty,
45}
46
47impl Data {
48 pub(crate) fn get(&mut self) -> Bytes {
49 match mem::replace(self, Data::Empty) {
50 Data::Chunk(data) => data,
51 Data::MutChunk(data) => data.freeze(),
52 Data::Empty => Bytes::new(),
53 }
54 }
55
56 pub(crate) fn as_slice(&self) -> &[u8] {
57 match self {
58 Data::Chunk(data) => data,
59 Data::MutChunk(data) => data,
60 Data::Empty => &[],
61 }
62 }
63
64 pub(crate) fn truncate(&mut self, len: usize) {
65 match self {
66 Data::Chunk(data) => data.truncate(len),
67 Data::MutChunk(data) => data.truncate(len),
68 Data::Empty => {}
69 }
70 }
71
72 pub(crate) fn push(&mut self, data: Bytes, limit: usize) {
78 if data.is_empty() {
79 return;
80 }
81 let buf = match mem::replace(self, Data::Empty) {
82 Data::Chunk(cur) => {
83 let len = cur.len() + data.len();
85 let size = message_size(&cur, &data, limit).map_or(len, |s| s.max(len));
86 let mut buf = BytesMut::with_capacity(size);
87 buf.extend_from_slice(&cur);
88 buf.extend_from_slice(&data);
89 buf
90 }
91 Data::MutChunk(mut buf) => {
92 if let Some(size) = message_size(&buf, &data, limit) {
93 buf.reserve_exact(size.saturating_sub(buf.len()));
94 }
95 buf.extend_from_slice(&data);
96 buf
97 }
98 Data::Empty => {
99 *self = Data::Chunk(data);
100 return;
101 }
102 };
103 *self = Data::MutChunk(buf);
104 }
105}
106
107fn message_size(cur: &[u8], data: &[u8], limit: usize) -> Option<usize> {
110 let mut prefix = cur.iter().chain(data).copied();
111 let (_, a, b, c, d) = (
112 prefix.next()?,
113 prefix.next()?,
114 prefix.next()?,
115 prefix.next()?,
116 prefix.next()?,
117 );
118 let len = u32::from_be_bytes([a, b, c, d]) as usize;
119 (len <= limit).then_some(5 + len)
120}
121
122pub(crate) fn prepend_prefix(msg: &mut BytePages, compressed: bool, len: u32) {
125 let [a, b, c, d] = len.to_be_bytes();
126 msg.prepend(Bytes::copy_from_slice(&[u8::from(compressed), a, b, c, d]));
127}
128
129pub(crate) fn send_size(len: usize, max_size: usize) -> Result<u32, HeaderValue> {
132 let msg = if len > max_size {
133 format!("grpc: trying to send message larger than max ({len} vs. {max_size})")
134 } else if let Ok(len) = u32::try_from(len) {
135 return Ok(len);
136 } else {
137 format!("grpc: message too large ({len} bytes)")
138 };
139 Err(HeaderValue::try_from(msg)
140 .unwrap_or_else(|_| HeaderValue::from_static("grpc: message too large")))
141}
142
143pub(crate) async fn frame_message(
149 mut msg: BytePages,
150 max_size: usize,
151 enc: Option<Enc>,
152) -> Result<BytePages, (GrpcStatus, HeaderValue)> {
153 let compressed = match enc {
154 Some(enc) => {
155 send_size(msg.len(), usize::MAX).map_err(|m| (GrpcStatus::ResourceExhausted, m))?;
157 enc.compress(&mut msg).await?
158 }
159 None => false,
160 };
161 let len = send_size(msg.len(), max_size).map_err(|m| (GrpcStatus::ResourceExhausted, m))?;
162 prepend_prefix(&mut msg, compressed, len);
163 Ok(msg)
164}
165
166pub fn encode_binary_header(value: &[u8]) -> HeaderValue {
176 let encoded = STANDARD_NO_PAD.encode(value);
177 unsafe { HeaderValue::from_shared_unchecked(encoded.into()) }
179}
180
181pub fn decode_binary_header(value: &HeaderValue) -> Option<Vec<u8>> {
195 decode_base64(value.as_bytes())
196}
197
198pub fn decode_binary_header_values(value: &HeaderValue) -> Option<Vec<Vec<u8>>> {
212 value
213 .as_bytes()
214 .split(|b| *b == b',')
215 .map(|val| decode_base64(val.trim_ascii()))
216 .collect()
217}
218
219fn decode_base64(value: &[u8]) -> Option<Vec<u8>> {
221 if value.len().is_multiple_of(4) {
223 STANDARD.decode(value).ok()
224 } else {
225 STANDARD_NO_PAD.decode(value).ok()
226 }
227}
228
229enum FlagError {
231 Unsupported(HeaderValue),
233 Invalid(HeaderValue),
235}
236
237fn check_compressed_flag(flag: u8, enc: Option<&HeaderValue>) -> Result<(), FlagError> {
239 match flag {
240 0 => Ok(()),
241 1 => match enc {
242 Some(enc) if !enc.as_bytes().eq_ignore_ascii_case(b"identity") => Err(
243 FlagError::Unsupported(grpc_message("Unsupported grpc-encoding", enc)),
244 ),
245 _ => Err(FlagError::Invalid(HeaderValue::from_static(
246 "Compressed message without grpc-encoding",
247 ))),
248 },
249 _ => Err(FlagError::Invalid(
250 HeaderValue::try_from(format!("Invalid compressed flag {flag}"))
251 .unwrap_or_else(|_| HeaderValue::from_static("Invalid compressed flag")),
252 )),
253 }
254}
255
256#[cfg_attr(not(feature = "compression"), allow(clippy::unused_async))]
262pub(crate) async fn read_message(
263 flag: u8,
264 enc: Option<HeaderValue>,
265 block: Bytes,
266 max_size: usize,
267 unsupported: GrpcStatus,
268) -> Result<Bytes, (GrpcStatus, HeaderValue)> {
269 #[cfg(feature = "compression")]
270 if flag == 1
271 && let Some(enc) = enc.as_ref().and_then(Enc::from_header)
272 {
273 return enc.decompress(block, max_size).await;
274 }
275 #[cfg(not(feature = "compression"))]
276 let _ = max_size;
277
278 match check_compressed_flag(flag, enc.as_ref()) {
279 Ok(()) => Ok(block),
280 Err(FlagError::Unsupported(msg)) => Err((unsupported, msg)),
281 Err(FlagError::Invalid(msg)) => Err((GrpcStatus::Internal, msg)),
282 }
283}
284
285pub(crate) fn is_grpc_content_type(ct: &[u8]) -> bool {
287 let prefix = b"application/grpc";
288 ct.len() >= prefix.len()
289 && ct[..prefix.len()].eq_ignore_ascii_case(prefix)
290 && matches!(ct.get(prefix.len()), None | Some(b'+' | b';'))
291}
292
293pub fn encode_grpc_message(msg: &str) -> HeaderValue {
304 let mut encoded = String::with_capacity(msg.len());
305 for b in msg.bytes() {
306 if b == b'%' || !(b' '..=b'~').contains(&b) {
307 let _ = write!(encoded, "%{b:02X}");
308 } else {
309 encoded.push(char::from(b));
310 }
311 }
312 unsafe { HeaderValue::from_shared_unchecked(encoded.into()) }
314}
315
316pub(crate) fn grpc_message(prefix: &'static str, val: &HeaderValue) -> HeaderValue {
320 match val.to_str() {
321 Ok(v) => encode_grpc_message(&format!("{prefix}: {v}")),
322 Err(_) => encode_grpc_message(prefix),
323 }
324}
325
326pub(crate) fn percent_decode(val: &HeaderValue) -> Cow<'_, str> {
331 match val.to_str() {
332 Ok(s) => unquote(s, Component::Opaque),
333 Err(_) => String::from_utf8_lossy(val.as_bytes()),
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[test]
343 fn prefix() {
344 let mut msg = BytePages::default();
345 msg.extend_from_slice(&[7; 100]);
346 let body = msg.take().unwrap();
347 let ptr = body.as_ref().as_ptr();
348 msg.append(body);
349
350 prepend_prefix(&mut msg, true, 0x0102_0304);
351 assert_eq!(msg.len(), 105);
352 assert_eq!(msg.take().unwrap().as_ref(), &[1, 1, 2, 3, 4]);
353 let body = msg.take().unwrap();
355 assert_eq!(body.as_ref().as_ptr(), ptr);
356 assert_eq!(body.as_ref(), &[7; 100]);
357 assert!(msg.take().is_none());
358 }
359
360 fn push(data: &mut Data, chunk: &[u8]) {
361 data.push(Bytes::copy_from_slice(chunk), 100);
362 }
363
364 fn capacity(data: &Data) -> usize {
365 let Data::MutChunk(buf) = data else {
366 panic!("not a buffer")
367 };
368 buf.capacity()
369 }
370
371 #[test]
372 fn data_push() {
373 let msg = [&[0, 0, 0, 0, 100][..], &[1; 100]].concat();
374
375 let chunk = Bytes::copy_from_slice(&msg);
377 let mut data = Data::Empty;
378 data.push(chunk.clone(), 100);
379 data.push(Bytes::new(), 100);
380 assert_eq!(data.get().as_ptr(), chunk.as_ptr());
381
382 for split in [&[10, 50][..], &[2, 4, 50], &[1, 1, 1, 1, 1, 50]] {
384 let mut data = Data::Empty;
385 let mut pos = 0;
386 for &end in split {
387 push(&mut data, &msg[pos..end]);
388 pos = end;
389 }
390 assert_eq!(capacity(&data), 105);
391 let ptr = data.as_slice().as_ptr();
392 push(&mut data, &msg[pos..]);
393 assert_eq!(capacity(&data), 105);
394 assert_eq!(data.as_slice().as_ptr(), ptr);
395 assert_eq!(data.get(), msg);
396 }
397
398 let mut data = Data::Empty;
400 push(&mut data, &msg[..2]);
401 push(&mut data, &msg[2..4]);
402 assert_eq!(capacity(&data), 4);
403
404 let mut data = Data::Empty;
406 push(&mut data, &msg[..60]);
407 push(&mut data, &msg[60..70]);
408 assert_eq!(capacity(&data), 105);
409
410 let big = [&[0, 0, 0, 0, 101][..], &[1; 101]].concat();
412 let mut data = Data::Empty;
413 push(&mut data, &big[..10]);
414 push(&mut data, &big[10..20]);
415 assert!(capacity(&data) < 106);
416 push(&mut data, &big[20..]);
417 assert_eq!(data.get(), big);
418
419 let msg = [&msg[..], &[1; 20]].concat();
421 let mut data = Data::Empty;
422 push(&mut data, &msg[..10]);
423 push(&mut data, &msg[10..]);
424 assert_eq!(capacity(&data), 125);
425 assert_eq!(data.get(), msg);
426 }
427
428 #[test]
429 fn binary_header() {
430 assert_eq!(encode_binary_header(b""), "");
431 assert_eq!(encode_binary_header(b"a"), "YQ");
432 assert_eq!(encode_binary_header(b"ab"), "YWI");
433 assert_eq!(encode_binary_header(b"abc"), "YWJj");
434
435 let dec = |v: &'static str| decode_binary_header(&HeaderValue::from_static(v));
436 assert_eq!(dec("YQ").unwrap(), b"a");
437 assert_eq!(dec("YQ==").unwrap(), b"a");
438 assert_eq!(dec("YWI=").unwrap(), b"ab");
439 assert_eq!(dec("YWJj").unwrap(), b"abc");
440 assert_eq!(dec("").unwrap(), b"");
441 assert_eq!(dec("/+8").unwrap(), [0xff, 0xef]);
442 assert!(dec("_-8").is_none());
444 assert!(dec("YQ=").is_none());
445 assert!(dec("Y").is_none());
446 assert!(dec("YQ=\t").is_none());
447 assert!(dec("YQ,YQ").is_none());
448
449 let dec = |v: &'static str| decode_binary_header_values(&HeaderValue::from_static(v));
450 assert_eq!(dec("").unwrap(), [b""]);
451 assert_eq!(dec("YQ").unwrap(), [b"a"]);
452 assert_eq!(dec("YQ==,YWI").unwrap(), [&b"a"[..], b"ab"]);
453 assert_eq!(dec("YQ , YWJj,\tYWI=").unwrap(), [&b"a"[..], b"abc", b"ab"]);
454 assert_eq!(dec("YQ,").unwrap(), [&b"a"[..], b""]);
455 assert!(dec("YQ,Y").is_none());
456 assert!(dec("YQ;YQ").is_none());
457 }
458
459 #[test]
460 fn encode_message() {
461 assert_eq!(encode_grpc_message(""), "");
462 assert_eq!(encode_grpc_message("plain text ~!"), "plain text ~!");
463 assert_eq!(encode_grpc_message("100%"), "100%25");
464 assert_eq!(encode_grpc_message("a\tb\r\n\x7f"), "a%09b%0D%0A%7F");
465 assert_eq!(encode_grpc_message("\u{20ac}"), "%E2%82%AC");
466
467 for msg in ["", "100% done", "a\nb", "caf\u{e9} \u{20ac}", "%41%zz"] {
469 assert_eq!(percent_decode(&encode_grpc_message(msg)), msg);
470 }
471 }
472
473 #[test]
474 fn message_with_value() {
475 let val = |v: &'static [u8]| grpc_message("bad", &HeaderValue::from_bytes(v).unwrap());
476 assert_eq!(val(b"text/html"), "bad: text/html");
477 assert_eq!(val(b"100%\tx"), "bad: 100%25%09x");
478 assert_eq!(val("caf\u{e9}".as_bytes()), "bad: caf%C3%A9");
479 assert_eq!(val(b"caf\xe9"), "bad");
481 }
482
483 #[test]
484 fn percent_decode_message() {
485 let dec = |v: &'static str| percent_decode(&HeaderValue::from_static(v)).into_owned();
486 let plain = HeaderValue::from_static("plain text");
487 assert!(matches!(
488 percent_decode(&plain),
489 Cow::Borrowed("plain text")
490 ));
491 assert_eq!(dec("100%25 done"), "100% done");
492 assert_eq!(dec("a%0Ab%0a"), "a\nb\n");
493 assert_eq!(dec("%E2%82%AC"), "\u{20ac}");
494 assert_eq!(dec("a+b"), "a+b");
496 assert_eq!(dec("%"), "%");
498 assert_eq!(dec("%4"), "%4");
499 assert_eq!(dec("%zz%41"), "%zzA");
500 assert_eq!(dec("%FFok"), "%FFok");
501 let raw = HeaderValue::from_bytes(b"caf\xc3\xa9").unwrap();
503 assert_eq!(percent_decode(&raw), "caf\u{e9}");
504 let raw = HeaderValue::from_bytes(b"caf\xe9").unwrap();
506 assert_eq!(percent_decode(&raw), "caf\u{fffd}");
507 }
508
509 #[test]
510 fn data_slice() {
511 let mut data = Data::Empty;
512 assert!(data.as_slice().is_empty());
513 data.truncate(5);
514 assert!(data.get().is_empty());
515
516 let mut data = Data::Chunk(Bytes::from_static(b"abcdef"));
517 assert_eq!(data.as_slice(), b"abcdef");
518 data.truncate(3);
519 assert_eq!(data.get(), Bytes::from_static(b"abc"));
520
521 let mut data = Data::Empty;
522 push(&mut data, b"abcdef");
523 data.truncate(2);
524 assert_eq!(data.as_slice(), b"ab");
525 assert_eq!(data.get(), Bytes::from_static(b"ab"));
526 }
527}