Skip to main content

tungstenite/extensions/compression/
mod.rs

1//! [Per-Message Compression Extensions][rfc7692]
2//!
3//! [rfc7692]: https://tools.ietf.org/html/rfc7692
4
5use bytes::Bytes;
6use thiserror::Error;
7
8#[cfg(feature = "deflate")]
9pub mod deflate;
10
11/// Active context for performing per-message compression.
12#[derive(Debug)]
13#[cfg_attr(not(feature = "deflate"), allow(missing_copy_implementations))] // This is only trivially copyable if compression is disabled.
14pub enum PerMessageCompressionContext {
15    /// Context for compressing/decompressing with `permessage-deflate`.
16    #[cfg(feature = "deflate")]
17    Deflate(deflate::DeflateContext),
18}
19
20/// Error encountered while compressing or decompressing.
21#[derive(Copy, Clone, Debug, Error, PartialEq, Eq)]
22pub enum CompressionError {
23    /// Error encountered while deflating or inflating
24    #[error("Deflate error: {0}")]
25    #[cfg(feature = "deflate")]
26    Deflate(deflate::DeflateError),
27}
28
29#[derive(Debug, Error)]
30#[cfg_attr(test, derive(PartialEq))]
31pub(crate) enum DecompressionError<E = CompressionError> {
32    /// The decompressed frame is larger than the configured limit.
33    #[error("decompressed data is too large")]
34    SizeLimitReached,
35    /// An error was encountered while decompressing.
36    #[error("{0}")]
37    Decompression(E),
38}
39
40impl PerMessageCompressionContext {
41    #[inline]
42    pub(crate) fn compressor<'s>(
43        &'s mut self,
44    ) -> impl FnMut(&Bytes) -> Result<Bytes, CompressionError> + 's {
45        move |payload: &Bytes| match self {
46            #[cfg(feature = "deflate")]
47            Self::Deflate(deflate_config) => {
48                deflate_config.compress(payload).map_err(CompressionError::Deflate)
49            }
50            #[cfg(not(feature = "deflate"))]
51            _ => {
52                let _ = payload;
53                unreachable!("*PerMessageCompressionContext is uninhabited")
54            }
55        }
56    }
57
58    #[inline]
59    pub(crate) fn decompressor<'s>(
60        &'s mut self,
61    ) -> impl FnMut(&Bytes, bool, usize) -> Result<Bytes, DecompressionError> + 's {
62        move |payload, is_final, size_limit| match self {
63            #[cfg(feature = "deflate")]
64            Self::Deflate(deflate_config) => deflate_config
65                .decompress(payload, is_final, size_limit)
66                .map_err(|e| e.map(CompressionError::Deflate)),
67            #[cfg(not(feature = "deflate"))]
68            _ => {
69                let _ = (payload, is_final, size_limit);
70                unreachable!("*PerMessageCompressionContext is uninhabited")
71            }
72        }
73    }
74}
75
76impl<E> DecompressionError<E> {
77    pub(crate) fn map<T>(self, f: impl FnOnce(E) -> T) -> DecompressionError<T> {
78        match self {
79            Self::SizeLimitReached => DecompressionError::SizeLimitReached,
80            Self::Decompression(e) => DecompressionError::Decompression(f(e)),
81        }
82    }
83}
84
85impl<E: Into<std::io::Error>> From<E> for DecompressionError<std::io::Error> {
86    fn from(value: E) -> Self {
87        Self::Decompression(value.into())
88    }
89}