1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-02-21 11:54:47 +01:00
This commit is contained in:
Park Joon-Kyu 2024-11-06 21:57:48 +09:00
parent c97b6ac0d4
commit 5bab156700
3 changed files with 89 additions and 39 deletions

View File

@ -80,7 +80,7 @@ bitflags! {
}
/// WebSocket message encoder.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Encoder {
flags: Flags,
@ -283,7 +283,7 @@ impl codec::Encoder<Message> for Encoder {
}
/// WebSocket message decoder.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Decoder {
flags: Flags,
max_size: usize,
@ -461,12 +461,45 @@ impl codec::Decoder for Decoder {
}
/// WebSocket protocol codec.
#[derive(Debug, Default, Clone)]
///
/// # Note
/// Cloning [`Codec`] creates a new codec with existing configurations
/// and will not preserve the current context.
#[derive(Debug, Default)]
pub struct Codec {
encoder: Encoder,
decoder: Decoder,
}
impl Clone for Codec {
fn clone(&self) -> Self {
Self {
encoder: Encoder {
flags: self.encoder.flags & Flags::SERVER,
#[cfg(feature = "compress-ws-deflate")]
deflate_compress: self.encoder.deflate_compress.as_ref().map(|c| {
DeflateCompressionContext::new(
Some(c.compression_level),
c.remote_no_context_takeover,
c.remote_max_window_bits,
)
}),
},
decoder: Decoder {
flags: self.decoder.flags & Flags::SERVER,
max_size: self.decoder.max_size,
#[cfg(feature = "compress-ws-deflate")]
deflate_decompress: self.decoder.deflate_decompress.as_ref().map(|d| {
DeflateDecompressionContext::new(
d.local_no_context_takeover,
d.local_max_window_bits,
)
}),
},
}
}
}
impl Codec {
/// Create new WebSocket frames codec.
pub fn new() -> Codec {

View File

@ -1,3 +1,7 @@
//! WebSocket permessage-deflate compression implementation.
//!
//!
use std::convert::Infallible;
use bytes::Bytes;
@ -6,17 +10,31 @@ pub use flate2::Compression as DeflateCompressionLevel;
use super::{OpCode, ProtocolError, RsvBits};
use crate::header::{HeaderName, HeaderValue, TryIntoHeaderPair, SEC_WEBSOCKET_EXTENSIONS};
// NOTE: according to [RFC 7692 §7.1.2.1] window bit size should be within 8..=15
// but we have to limit the range to 9..=15 because [flate2] only supports window bit within 9..=15.
//
// [RFC 6792]: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1.2.1
// [flate2]: https://docs.rs/flate2/latest/flate2/struct.Compress.html#method.new_with_window_bits
const MAX_WINDOW_BITS_RANGE: std::ops::RangeInclusive<u8> = 9..=15;
const DEFAULT_WINDOW_BITS: u8 = 15;
const BUF_SIZE: usize = 2048;
pub(super) const RSV_BIT_DEFLATE_FLAG: RsvBits = RsvBits::RSV1;
/// DEFLATE compression related handshake errors.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DeflateHandshakeError {
/// Unknown extension parameter given.
UnknownWebSocketParameters,
/// Duplicate parameter found in single extension statement.
DuplicateParameter(&'static str),
/// Max window bits size out of range. Should be in 9..=15
MaxWindowBitsOutOfRange,
/// Multiple `permessage-deflate` statements found but failed to negotiate any.
NoSuitableConfigurationFound,
}
@ -45,17 +63,28 @@ impl std::fmt::Display for DeflateHandshakeError {
impl std::error::Error for DeflateHandshakeError {}
/// Maximum size of client's DEFLATE sliding window.
#[derive(Copy, Clone, Debug)]
pub enum ClientMaxWindowBits {
/// Unspecified. Indicates server should decide its size.
NotSpecified,
/// Specified size of client's DEFLATE sliding window size in bits, between 9 and 15.
Specified(u8),
}
/// DEFLATE negotiation parameter. It can be used both client and server side.
/// At client side, it can be used to pass desired configuration to server.
/// At server side, negotiated parameter will be sent to client with this.
/// This can be represented in HTTP header form as it implements [`TryIntoHeaderPair`] trait.
#[derive(Debug, Clone, Default)]
pub struct DeflateSessionParameters {
/// Disallow server from take over context.
pub server_no_context_takeover: bool,
/// Disallow client from take over context.
pub client_no_context_takeover: bool,
/// Maximum size of server's DEFLATE sliding window in bits, between 9 and 15.
pub server_max_window_bits: Option<u8>,
/// Maximum size of client's DEFLATE sliding window.
pub client_max_window_bits: Option<ClientMaxWindowBits>,
}
@ -219,8 +248,10 @@ impl DeflateSessionParameters {
}
}
/// Server-side DEFLATE configuration.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DeflateServerConfig {
/// DEFLATE compression level. See [`flate2::`]
pub compression_level: Option<DeflateCompressionLevel>,
pub server_no_context_takeover: bool,
@ -294,15 +325,8 @@ pub struct DeflateDecompressionContext {
total_bytes_read: u64,
}
impl Clone for DeflateDecompressionContext {
fn clone(&self) -> Self {
// Create with empty context because the context is not meant to be cloned.
Self::new(self.local_no_context_takeover, self.local_max_window_bits)
}
}
impl DeflateDecompressionContext {
fn new(local_no_context_takeover: bool, local_max_window_bits: u8) -> Self {
pub(super) fn new(local_no_context_takeover: bool, local_max_window_bits: u8) -> Self {
Self {
local_no_context_takeover,
local_max_window_bits,
@ -315,7 +339,11 @@ impl DeflateDecompressionContext {
}
}
pub fn reset_with(&mut self, local_no_context_takeover: bool, local_max_window_bits: u8) {
pub(super) fn reset_with(
&mut self,
local_no_context_takeover: bool,
local_max_window_bits: u8,
) {
*self = Self::new(local_no_context_takeover, local_max_window_bits);
}
@ -352,16 +380,16 @@ impl DeflateDecompressionContext {
&mut buf,
flate2::FlushDecompress::Finish,
)
.map_err(|e| {
.map_err(|err| {
self.reset();
ProtocolError::Io(e.into())
ProtocolError::Io(err.into())
})?
} else {
self.decompress
.decompress(&payload[offset..], &mut buf, flate2::FlushDecompress::None)
.map_err(|e| {
.map_err(|err| {
self.reset();
ProtocolError::Io(e.into())
ProtocolError::Io(err.into())
})?
};
@ -399,7 +427,7 @@ impl DeflateDecompressionContext {
#[derive(Debug)]
pub struct DeflateCompressionContext {
compression_level: flate2::Compression,
pub(super) compression_level: flate2::Compression,
pub(super) remote_no_context_takeover: bool,
pub(super) remote_max_window_bits: u8,
@ -408,19 +436,8 @@ pub struct DeflateCompressionContext {
total_bytes_read: u64,
}
impl Clone for DeflateCompressionContext {
fn clone(&self) -> Self {
// Create with empty context because the context is not meant to be cloned.
Self::new(
Some(self.compression_level),
self.remote_no_context_takeover,
self.remote_max_window_bits,
)
}
}
impl DeflateCompressionContext {
fn new(
pub(super) fn new(
compression_level: Option<flate2::Compression>,
remote_no_context_takeover: bool,
remote_max_window_bits: u8,
@ -443,7 +460,7 @@ impl DeflateCompressionContext {
}
}
pub fn reset_with(
pub(super) fn reset_with(
mut self,
remote_no_context_takeover: bool,
remote_max_window_bits: u8,
@ -466,16 +483,16 @@ impl DeflateCompressionContext {
let res = if total_in >= payload.len() as u64 {
self.compress
.compress(&[], &mut buf, flate2::FlushCompress::Sync)
.map_err(|e| {
.map_err(|err| {
self.reset();
ProtocolError::Io(e.into())
ProtocolError::Io(err.into())
})?
} else {
self.compress
.compress(&payload, &mut buf, flate2::FlushCompress::None)
.map_err(|e| {
.map_err(|err| {
self.reset();
ProtocolError::Io(e.into())
ProtocolError::Io(err.into())
})?
};

View File

@ -100,7 +100,7 @@ pub enum HandshakeError {
/// Invalid `permessage-deflate` request.
#[cfg(feature = "compress-ws-deflate")]
#[display(fmt = "invalid WebSocket `permessage-deflate` extension request")]
#[display("invalid WebSocket `permessage-deflate` extension request")]
BadDeflateRequest(deflate::DeflateHandshakeError),
}
@ -190,13 +190,13 @@ pub fn handshake_with_deflate(
let mut selected_error = None;
for config in available_configurations {
match config {
Ok(v) => {
selected_config = Some(v);
Ok(config) => {
selected_config = Some(config);
break;
}
Err(e) => {
Err(err) => {
if selected_error.is_none() {
selected_error = Some(e);
selected_error = Some(err);
} else {
selected_error =
Some(deflate::DeflateHandshakeError::NoSuitableConfigurationFound);