|
|
|
@ -18,12 +18,195 @@ use http::{HeaderValue, Request, Response}; |
|
|
|
|
use std::mem::replace; |
|
|
|
|
use std::slice; |
|
|
|
|
|
|
|
|
|
const EXT_NAME: &str = "permessage-deflate"; |
|
|
|
|
|
|
|
|
|
/// A permessage-deflate configuration.
|
|
|
|
|
#[derive(Clone, Copy, Debug)] |
|
|
|
|
pub struct DeflateConfig { |
|
|
|
|
/// The maximum size of a message. `None` means no size limit. The default value is 64 MiB
|
|
|
|
|
/// which should be reasonably big for all normal use-cases but small enough to prevent
|
|
|
|
|
/// memory eating by a malicious user.
|
|
|
|
|
max_message_size: Option<usize>, |
|
|
|
|
/// The LZ77 sliding window size. Negotiated during the HTTP upgrade. In client mode, this
|
|
|
|
|
/// conforms to RFC 7692 7.1.2.1. In server mode, this conforms to RFC 7692 7.1.2.2. Must be in
|
|
|
|
|
/// range 9..=15.
|
|
|
|
|
max_window_bits: u8, |
|
|
|
|
/// Request that the server resets the LZ77 sliding window between messages - RFC 7692 7.1.1.1.
|
|
|
|
|
request_no_context_takeover: bool, |
|
|
|
|
accept_no_context_takeover: bool, |
|
|
|
|
compress_reset: bool, |
|
|
|
|
decompress_reset: bool, |
|
|
|
|
compression_level: Compression, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl DeflateConfig { |
|
|
|
|
/// Builds a new `DeflateConfig` using the `compression_level` and the defaults for all other
|
|
|
|
|
/// members.
|
|
|
|
|
pub fn with_compression_level(compression_level: Compression) -> DeflateConfig { |
|
|
|
|
DeflateConfig { |
|
|
|
|
compression_level, |
|
|
|
|
..Default::default() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the maximum message size permitted.
|
|
|
|
|
pub fn max_message_size(&self) -> Option<usize> { |
|
|
|
|
self.max_message_size |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the maximum LZ77 window size permitted.
|
|
|
|
|
pub fn max_window_bits(&self) -> u8 { |
|
|
|
|
self.max_window_bits |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns whether `no_context_takeover` has been requested.
|
|
|
|
|
pub fn request_no_context_takeover(&self) -> bool { |
|
|
|
|
self.request_no_context_takeover |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns whether this WebSocket will accept `no_context_takeover`.
|
|
|
|
|
pub fn accept_no_context_takeover(&self) -> bool { |
|
|
|
|
self.accept_no_context_takeover |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns whether or not the inner compressor is set to reset after completing a message.
|
|
|
|
|
pub fn compress_reset(&self) -> bool { |
|
|
|
|
self.compress_reset |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns whether or not the inner decompressor is set to reset after completing a message.
|
|
|
|
|
pub fn decompress_reset(&self) -> bool { |
|
|
|
|
self.decompress_reset |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the active compression level.
|
|
|
|
|
pub fn compression_level(&self) -> Compression { |
|
|
|
|
self.compression_level |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the maximum message size permitted.
|
|
|
|
|
pub fn set_max_message_size(&mut self, max_message_size: Option<usize>) { |
|
|
|
|
self.max_message_size = max_message_size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the LZ77 sliding window size.
|
|
|
|
|
pub fn set_max_window_bits(&mut self, max_window_bits: u8) { |
|
|
|
|
assert!((9u8..=15u8).contains(&max_window_bits)); |
|
|
|
|
self.max_window_bits = max_window_bits; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the WebSocket to request `no_context_takeover` if `true`.
|
|
|
|
|
pub fn set_request_no_context_takeover(&mut self, request_no_context_takeover: bool) { |
|
|
|
|
self.request_no_context_takeover = request_no_context_takeover; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the WebSocket to accept `no_context_takeover` if `true`.
|
|
|
|
|
pub fn set_accept_no_context_takeover(&mut self, accept_no_context_takeover: bool) { |
|
|
|
|
self.accept_no_context_takeover = accept_no_context_takeover; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Default for DeflateConfig { |
|
|
|
|
fn default() -> Self { |
|
|
|
|
DeflateConfig { |
|
|
|
|
max_message_size: Some(MAX_MESSAGE_SIZE), |
|
|
|
|
max_window_bits: 15, |
|
|
|
|
request_no_context_takeover: false, |
|
|
|
|
accept_no_context_takeover: true, |
|
|
|
|
compress_reset: false, |
|
|
|
|
decompress_reset: false, |
|
|
|
|
compression_level: Compression::best(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A `DeflateConfig` builder.
|
|
|
|
|
#[derive(Debug, Copy, Clone)] |
|
|
|
|
pub struct DeflateConfigBuilder { |
|
|
|
|
max_message_size: Option<usize>, |
|
|
|
|
max_window_bits: u8, |
|
|
|
|
request_no_context_takeover: bool, |
|
|
|
|
accept_no_context_takeover: bool, |
|
|
|
|
fragments_grow: bool, |
|
|
|
|
compression_level: Compression, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Default for DeflateConfigBuilder { |
|
|
|
|
fn default() -> Self { |
|
|
|
|
DeflateConfigBuilder { |
|
|
|
|
max_message_size: Some(MAX_MESSAGE_SIZE), |
|
|
|
|
max_window_bits: 15, |
|
|
|
|
request_no_context_takeover: false, |
|
|
|
|
accept_no_context_takeover: true, |
|
|
|
|
fragments_grow: true, |
|
|
|
|
compression_level: Compression::fast(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl DeflateConfigBuilder { |
|
|
|
|
/// Sets the maximum message size permitted.
|
|
|
|
|
pub fn max_message_size(mut self, max_message_size: Option<usize>) -> DeflateConfigBuilder { |
|
|
|
|
self.max_message_size = max_message_size; |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the LZ77 sliding window size. Panics if the provided size is not in `9..=15`.
|
|
|
|
|
pub fn max_window_bits(mut self, max_window_bits: u8) -> DeflateConfigBuilder { |
|
|
|
|
assert!( |
|
|
|
|
(9u8..=15u8).contains(&max_window_bits), |
|
|
|
|
"max window bits must be in range 9..=15" |
|
|
|
|
); |
|
|
|
|
self.max_window_bits = max_window_bits; |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the WebSocket to request `no_context_takeover`.
|
|
|
|
|
pub fn request_no_context_takeover( |
|
|
|
|
mut self, |
|
|
|
|
request_no_context_takeover: bool, |
|
|
|
|
) -> DeflateConfigBuilder { |
|
|
|
|
self.request_no_context_takeover = request_no_context_takeover; |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the WebSocket to accept `no_context_takeover`.
|
|
|
|
|
pub fn accept_no_context_takeover( |
|
|
|
|
mut self, |
|
|
|
|
accept_no_context_takeover: bool, |
|
|
|
|
) -> DeflateConfigBuilder { |
|
|
|
|
self.accept_no_context_takeover = accept_no_context_takeover; |
|
|
|
|
self |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Consumes the builder and produces a `DeflateConfig.`
|
|
|
|
|
pub fn build(self) -> DeflateConfig { |
|
|
|
|
DeflateConfig { |
|
|
|
|
max_message_size: self.max_message_size, |
|
|
|
|
max_window_bits: self.max_window_bits, |
|
|
|
|
request_no_context_takeover: self.request_no_context_takeover, |
|
|
|
|
accept_no_context_takeover: self.accept_no_context_takeover, |
|
|
|
|
compression_level: self.compression_level, |
|
|
|
|
..Default::default() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A permessage-deflate encoding WebSocket extension.
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
pub struct DeflateExt { |
|
|
|
|
/// Defines whether the extension is enabled. Following a successful handshake, this will be
|
|
|
|
|
/// `true`.
|
|
|
|
|
enabled: bool, |
|
|
|
|
/// The configuration for the extension.
|
|
|
|
|
config: DeflateConfig, |
|
|
|
|
/// A stack of continuation frames awaiting `fin`.
|
|
|
|
|
fragments: Vec<Frame>, |
|
|
|
|
/// The deflate decompressor.
|
|
|
|
|
inflator: Inflator, |
|
|
|
|
/// The deflate compressor.
|
|
|
|
|
deflator: Deflator, |
|
|
|
|
/// If this deflate extension is not used, messages will be forwarded to this extension.
|
|
|
|
|
uncompressed_extension: PlainTextExt, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -34,8 +217,8 @@ impl Clone for DeflateExt { |
|
|
|
|
config: self.config, |
|
|
|
|
fragments: vec![], |
|
|
|
|
inflator: Inflator::new(), |
|
|
|
|
deflator: Deflator::new(self.config.compression_level), |
|
|
|
|
uncompressed_extension: PlainTextExt::new(self.config.max_message_size), |
|
|
|
|
deflator: Deflator::new(self.config.compression_level()), |
|
|
|
|
uncompressed_extension: PlainTextExt::new(self.config.max_message_size()), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -47,6 +230,7 @@ impl Default for DeflateExt { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl DeflateExt { |
|
|
|
|
/// Creates a `DeflateExt` instance using the provided configuration.
|
|
|
|
|
pub fn new(config: DeflateConfig) -> DeflateExt { |
|
|
|
|
DeflateExt { |
|
|
|
|
enabled: false, |
|
|
|
@ -54,7 +238,7 @@ impl DeflateExt { |
|
|
|
|
fragments: vec![], |
|
|
|
|
inflator: Inflator::new(), |
|
|
|
|
deflator: Deflator::new(Compression::fast()), |
|
|
|
|
uncompressed_extension: PlainTextExt::new(config.max_message_size), |
|
|
|
|
uncompressed_extension: PlainTextExt::new(config.max_message_size()), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -66,7 +250,7 @@ impl DeflateExt { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let mut incomplete_message = IncompleteMessage::new(message_type); |
|
|
|
|
incomplete_message.extend(data, self.config.max_message_size)?; |
|
|
|
|
incomplete_message.extend(data, self.config.max_message_size())?; |
|
|
|
|
incomplete_message.complete() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -82,16 +266,13 @@ impl DeflateExt { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if window_bits >= 9 && window_bits <= 15 { |
|
|
|
|
if window_bits as u8 != self.config.max_window_bits { |
|
|
|
|
if window_bits != self.config.max_window_bits() { |
|
|
|
|
Ok(Some(window_bits)) |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
Err(format!( |
|
|
|
|
"Invalid server_max_window_bits parameter: {}", |
|
|
|
|
window_bits |
|
|
|
|
)) |
|
|
|
|
Err(format!("Invalid window parameter: {}", window_bits)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Err(e) => Err(e.to_string()), |
|
|
|
@ -107,57 +288,29 @@ impl DeflateExt { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)] |
|
|
|
|
pub struct DeflateConfig { |
|
|
|
|
pub max_message_size: Option<usize>, |
|
|
|
|
pub max_window_bits: u8, |
|
|
|
|
pub request_no_context_takeover: bool, |
|
|
|
|
pub accept_no_context_takeover: bool, |
|
|
|
|
pub fragments_capacity: usize, |
|
|
|
|
pub fragments_grow: bool, |
|
|
|
|
pub compress_reset: bool, |
|
|
|
|
pub decompress_reset: bool, |
|
|
|
|
pub compression_level: Compression, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl DeflateConfig { |
|
|
|
|
pub fn with_compression_level(compression_level: Compression) -> DeflateConfig { |
|
|
|
|
DeflateConfig { |
|
|
|
|
compression_level, |
|
|
|
|
..Default::default() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Default for DeflateConfig { |
|
|
|
|
fn default() -> Self { |
|
|
|
|
DeflateConfig { |
|
|
|
|
max_message_size: Some(MAX_MESSAGE_SIZE), |
|
|
|
|
max_window_bits: 15, |
|
|
|
|
request_no_context_takeover: false, |
|
|
|
|
accept_no_context_takeover: true, |
|
|
|
|
fragments_capacity: 10, |
|
|
|
|
fragments_grow: true, |
|
|
|
|
compress_reset: false, |
|
|
|
|
decompress_reset: false, |
|
|
|
|
compression_level: Compression::best(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A permessage-deflate extension error.
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
|
pub enum DeflateExtensionError { |
|
|
|
|
/// An error produced when deflating a message.
|
|
|
|
|
DeflateError(String), |
|
|
|
|
/// An error produced when inflating a message.
|
|
|
|
|
InflateError(String), |
|
|
|
|
/// An error produced during the WebSocket negotiation.
|
|
|
|
|
NegotiationError(String), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Display for DeflateExtensionError { |
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
|
|
|
|
match self { |
|
|
|
|
DeflateExtensionError::DeflateError(m) => write!(f, "{}", m), |
|
|
|
|
DeflateExtensionError::InflateError(m) => write!(f, "{}", m), |
|
|
|
|
DeflateExtensionError::NegotiationError(m) => write!(f, "{}", m), |
|
|
|
|
DeflateExtensionError::DeflateError(m) => { |
|
|
|
|
write!(f, "An error was produced during decompression: {}", m) |
|
|
|
|
} |
|
|
|
|
DeflateExtensionError::InflateError(m) => { |
|
|
|
|
write!(f, "An error was produced during compression: {}", m) |
|
|
|
|
} |
|
|
|
|
DeflateExtensionError::NegotiationError(m) => { |
|
|
|
|
write!(f, "An upgrade error was encountered: {}", m) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -176,21 +329,18 @@ impl From<InvalidHeaderValue> for DeflateExtensionError { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const EXT_NAME: &str = "permessage-deflate"; |
|
|
|
|
|
|
|
|
|
impl WebSocketExtension for DeflateExt { |
|
|
|
|
type Error = DeflateExtensionError; |
|
|
|
|
|
|
|
|
|
fn enabled(&self) -> bool { |
|
|
|
|
self.enabled |
|
|
|
|
fn new(max_message_size: Option<usize>) -> Self { |
|
|
|
|
DeflateExt::new(DeflateConfig { |
|
|
|
|
max_message_size, |
|
|
|
|
..Default::default() |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn rsv1(&self) -> bool { |
|
|
|
|
if self.enabled { |
|
|
|
|
true |
|
|
|
|
} else { |
|
|
|
|
self.uncompressed_extension.rsv1() |
|
|
|
|
} |
|
|
|
|
fn enabled(&self) -> bool { |
|
|
|
|
self.enabled |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn on_make_request<T>(&mut self, mut request: Request<T>) -> Request<T> { |
|
|
|
@ -228,147 +378,136 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
response: &mut Response<T>, |
|
|
|
|
) -> Result<(), Self::Error> { |
|
|
|
|
for header in request.headers().get_all(SEC_WEBSOCKET_EXTENSIONS) { |
|
|
|
|
match header.to_str() { |
|
|
|
|
return match header.to_str() { |
|
|
|
|
Ok(header) => { |
|
|
|
|
let mut res_ext = String::with_capacity(header.len()); |
|
|
|
|
let mut s_takeover = false; |
|
|
|
|
let mut c_takeover = false; |
|
|
|
|
let mut s_max = false; |
|
|
|
|
let mut c_max = false; |
|
|
|
|
let mut response_str = String::with_capacity(header.len()); |
|
|
|
|
let mut server_takeover = false; |
|
|
|
|
let mut client_takeover = false; |
|
|
|
|
let mut server_max_bits = false; |
|
|
|
|
let mut client_max_bits = false; |
|
|
|
|
|
|
|
|
|
for param in header.split(';') { |
|
|
|
|
match param.trim() { |
|
|
|
|
"permessage-deflate" => res_ext.push_str("permessage-deflate"), |
|
|
|
|
"permessage-deflate" => response_str.push_str("permessage-deflate"), |
|
|
|
|
"server_no_context_takeover" => { |
|
|
|
|
if s_takeover { |
|
|
|
|
if server_takeover { |
|
|
|
|
self.decline(response); |
|
|
|
|
} else { |
|
|
|
|
s_takeover = true; |
|
|
|
|
if self.config.accept_no_context_takeover { |
|
|
|
|
server_takeover = true; |
|
|
|
|
if self.config.accept_no_context_takeover() { |
|
|
|
|
self.config.compress_reset = true; |
|
|
|
|
res_ext.push_str("; server_no_context_takeover"); |
|
|
|
|
response_str.push_str("; server_no_context_takeover"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
"client_no_context_takeover" => { |
|
|
|
|
if c_takeover { |
|
|
|
|
if client_takeover { |
|
|
|
|
self.decline(response); |
|
|
|
|
} else { |
|
|
|
|
c_takeover = true; |
|
|
|
|
client_takeover = true; |
|
|
|
|
self.config.decompress_reset = true; |
|
|
|
|
res_ext.push_str("; client_no_context_takeover"); |
|
|
|
|
response_str.push_str("; client_no_context_takeover"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
param if param.starts_with("server_max_window_bits") => { |
|
|
|
|
if s_max { |
|
|
|
|
if server_max_bits { |
|
|
|
|
self.decline(response); |
|
|
|
|
} else { |
|
|
|
|
s_max = true; |
|
|
|
|
let mut param_iter = param.split('='); |
|
|
|
|
param_iter.next(); // we already know the name
|
|
|
|
|
if let Some(window_bits_str) = param_iter.next() { |
|
|
|
|
if let Ok(window_bits) = window_bits_str.trim().parse() { |
|
|
|
|
if window_bits >= 9 && window_bits <= 15 { |
|
|
|
|
if window_bits < self.config.max_window_bits { |
|
|
|
|
self.deflator = Deflator { |
|
|
|
|
compress: Compress::new_with_window_bits( |
|
|
|
|
self.config.compression_level, |
|
|
|
|
false, |
|
|
|
|
window_bits, |
|
|
|
|
), |
|
|
|
|
}; |
|
|
|
|
res_ext.push_str("; "); |
|
|
|
|
res_ext.push_str(param) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
self.decline(response); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
server_max_bits = true; |
|
|
|
|
|
|
|
|
|
match self.parse_window_parameter(param.split('=').skip(1)) { |
|
|
|
|
Ok(Some(bits)) => { |
|
|
|
|
self.deflator = Deflator { |
|
|
|
|
compress: Compress::new_with_window_bits( |
|
|
|
|
self.config.compression_level(), |
|
|
|
|
false, |
|
|
|
|
bits, |
|
|
|
|
), |
|
|
|
|
}; |
|
|
|
|
response_str.push_str("; "); |
|
|
|
|
response_str.push_str(param) |
|
|
|
|
} |
|
|
|
|
Ok(None) => {} |
|
|
|
|
Err(_) => { |
|
|
|
|
self.decline(response); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
param if param.starts_with("client_max_window_bits") => { |
|
|
|
|
if c_max { |
|
|
|
|
if client_max_bits { |
|
|
|
|
self.decline(response); |
|
|
|
|
} else { |
|
|
|
|
c_max = true; |
|
|
|
|
let mut param_iter = param.split('='); |
|
|
|
|
param_iter.next(); // we already know the name
|
|
|
|
|
if let Some(window_bits_str) = param_iter.next() { |
|
|
|
|
if let Ok(window_bits) = window_bits_str.trim().parse() { |
|
|
|
|
if window_bits >= 9 && window_bits <= 15 { |
|
|
|
|
if window_bits < self.config.max_window_bits { |
|
|
|
|
self.inflator = Inflator { |
|
|
|
|
decompress: |
|
|
|
|
Decompress::new_with_window_bits( |
|
|
|
|
false, |
|
|
|
|
window_bits, |
|
|
|
|
), |
|
|
|
|
}; |
|
|
|
|
res_ext.push_str("; "); |
|
|
|
|
res_ext.push_str(param); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
self.decline(response); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
client_max_bits = true; |
|
|
|
|
|
|
|
|
|
match self.parse_window_parameter(param.split('=').skip(1)) { |
|
|
|
|
Ok(Some(bits)) => { |
|
|
|
|
self.inflator = Inflator { |
|
|
|
|
decompress: Decompress::new_with_window_bits( |
|
|
|
|
false, bits, |
|
|
|
|
), |
|
|
|
|
}; |
|
|
|
|
response_str.push_str("; "); |
|
|
|
|
response_str.push_str(param); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
Ok(None) => {} |
|
|
|
|
Err(_) => { |
|
|
|
|
self.decline(response); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
res_ext.push_str("; "); |
|
|
|
|
res_ext.push_str(&format!( |
|
|
|
|
|
|
|
|
|
response_str.push_str("; "); |
|
|
|
|
response_str.push_str(&format!( |
|
|
|
|
"client_max_window_bits={}", |
|
|
|
|
self.config.max_window_bits |
|
|
|
|
self.config.max_window_bits() |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
_ => { |
|
|
|
|
// decline all extension offers because we got a bad parameter
|
|
|
|
|
self.decline(response); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !res_ext.contains("client_no_context_takeover") |
|
|
|
|
&& self.config.request_no_context_takeover |
|
|
|
|
if !response_str.contains("client_no_context_takeover") |
|
|
|
|
&& self.config.request_no_context_takeover() |
|
|
|
|
{ |
|
|
|
|
self.config.decompress_reset = true; |
|
|
|
|
res_ext.push_str("; client_no_context_takeover"); |
|
|
|
|
response_str.push_str("; client_no_context_takeover"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !res_ext.contains("server_max_window_bits") { |
|
|
|
|
res_ext.push_str("; "); |
|
|
|
|
res_ext.push_str(&format!( |
|
|
|
|
if !response_str.contains("server_max_window_bits") { |
|
|
|
|
response_str.push_str("; "); |
|
|
|
|
response_str.push_str(&format!( |
|
|
|
|
"server_max_window_bits={}", |
|
|
|
|
self.config.max_window_bits |
|
|
|
|
self.config.max_window_bits() |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !res_ext.contains("client_max_window_bits") |
|
|
|
|
&& self.config.max_window_bits < 15 |
|
|
|
|
if !response_str.contains("client_max_window_bits") |
|
|
|
|
&& self.config.max_window_bits() < 15 |
|
|
|
|
{ |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
response |
|
|
|
|
.headers_mut() |
|
|
|
|
.insert(SEC_WEBSOCKET_EXTENSIONS, HeaderValue::from_str(&res_ext)?); |
|
|
|
|
response.headers_mut().insert( |
|
|
|
|
SEC_WEBSOCKET_EXTENSIONS, |
|
|
|
|
HeaderValue::from_str(&response_str)?, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
self.enabled = true; |
|
|
|
|
|
|
|
|
|
return Ok(()); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
self.enabled = false; |
|
|
|
|
return Err(DeflateExtensionError::NegotiationError(format!( |
|
|
|
|
"Failed to parse header: {}", |
|
|
|
|
Err(DeflateExtensionError::NegotiationError(format!( |
|
|
|
|
"Failed to parse request header: {}", |
|
|
|
|
e, |
|
|
|
|
))); |
|
|
|
|
))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.decline(response); |
|
|
|
@ -382,7 +521,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
let mut server_max_window_bits = false; |
|
|
|
|
let mut client_max_window_bits = false; |
|
|
|
|
|
|
|
|
|
for header in response.headers().get_all(SEC_WEBSOCKET_EXTENSIONS) { |
|
|
|
|
for header in response.headers().get_all(SEC_WEBSOCKET_EXTENSIONS).iter() { |
|
|
|
|
match header.to_str() { |
|
|
|
|
Ok(header) => { |
|
|
|
|
for param in header.split(';') { |
|
|
|
@ -390,7 +529,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
"permessage-deflate" => { |
|
|
|
|
if extension_name { |
|
|
|
|
return Err(DeflateExtensionError::NegotiationError(format!( |
|
|
|
|
"Duplicate extension name permessage-deflate" |
|
|
|
|
"Duplicate extension parameter permessage-deflate" |
|
|
|
|
))); |
|
|
|
|
} else { |
|
|
|
|
self.enabled = true; |
|
|
|
@ -415,7 +554,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
} else { |
|
|
|
|
client_takeover = true; |
|
|
|
|
|
|
|
|
|
if self.config.accept_no_context_takeover { |
|
|
|
|
if self.config.accept_no_context_takeover() { |
|
|
|
|
self.config.compress_reset = true; |
|
|
|
|
} else { |
|
|
|
|
return Err(DeflateExtensionError::NegotiationError( |
|
|
|
@ -436,7 +575,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
Ok(Some(bits)) => { |
|
|
|
|
self.deflator = Deflator { |
|
|
|
|
compress: Compress::new_with_window_bits( |
|
|
|
|
self.config.compression_level, |
|
|
|
|
self.config.compression_level(), |
|
|
|
|
false, |
|
|
|
|
bits, |
|
|
|
|
), |
|
|
|
@ -493,7 +632,6 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
self.enabled = false; |
|
|
|
|
|
|
|
|
|
return Err(DeflateExtensionError::NegotiationError(format!( |
|
|
|
|
"Failed to parse extension parameter: {}", |
|
|
|
|
e |
|
|
|
@ -517,7 +655,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
*frame.payload_mut() = compressed; |
|
|
|
|
frame.header_mut().rsv1 = true; |
|
|
|
|
|
|
|
|
|
if self.config.compress_reset { |
|
|
|
|
if self.config.compress_reset() { |
|
|
|
|
self.deflator.reset(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -536,15 +674,7 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
return Ok(None); |
|
|
|
|
} else { |
|
|
|
|
let message = if let OpCode::Data(Data::Continue) = frame.header().opcode { |
|
|
|
|
if !self.config.fragments_grow |
|
|
|
|
&& self.config.fragments_capacity == self.fragments.len() |
|
|
|
|
{ |
|
|
|
|
return Err(DeflateExtensionError::DeflateError( |
|
|
|
|
"Exceeded max fragments.".into(), |
|
|
|
|
)); |
|
|
|
|
} else { |
|
|
|
|
self.fragments.push(frame); |
|
|
|
|
} |
|
|
|
|
self.fragments.push(frame); |
|
|
|
|
|
|
|
|
|
let opcode = self.fragments.first().unwrap().header().opcode; |
|
|
|
|
let size = self |
|
|
|
@ -554,14 +684,11 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
let mut compressed = Vec::with_capacity(size); |
|
|
|
|
let mut decompressed = Vec::with_capacity(size * 2); |
|
|
|
|
|
|
|
|
|
replace( |
|
|
|
|
&mut self.fragments, |
|
|
|
|
Vec::with_capacity(self.config.fragments_capacity), |
|
|
|
|
) |
|
|
|
|
.into_iter() |
|
|
|
|
.for_each(|f| { |
|
|
|
|
compressed.extend(f.into_data()); |
|
|
|
|
}); |
|
|
|
|
replace(&mut self.fragments, Vec::with_capacity(10)) |
|
|
|
|
.into_iter() |
|
|
|
|
.for_each(|f| { |
|
|
|
|
compressed.extend(f.into_data()); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
compressed.extend(&[0, 0, 255, 255]); |
|
|
|
|
|
|
|
|
@ -570,16 +697,14 @@ impl WebSocketExtension for DeflateExt { |
|
|
|
|
self.complete_message(decompressed, opcode) |
|
|
|
|
} else { |
|
|
|
|
frame.payload_mut().extend(&[0, 0, 255, 255]); |
|
|
|
|
|
|
|
|
|
let mut decompress_output = |
|
|
|
|
Vec::with_capacity(frame.payload().len() * 2); |
|
|
|
|
let mut decompressed = Vec::with_capacity(frame.payload().len() * 2); |
|
|
|
|
self.inflator |
|
|
|
|
.decompress(frame.payload(), &mut decompress_output)?; |
|
|
|
|
.decompress(frame.payload(), &mut decompressed)?; |
|
|
|
|
|
|
|
|
|
self.complete_message(decompress_output, frame.header().opcode) |
|
|
|
|
self.complete_message(decompressed, frame.header().opcode) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if self.config.decompress_reset { |
|
|
|
|
if self.config.decompress_reset() { |
|
|
|
|
self.inflator.reset(false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -610,6 +735,7 @@ impl From<CompressError> for DeflateExtensionError { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Deflator { |
|
|
|
|
compress: Compress, |
|
|
|
|
} |
|
|
|
@ -656,12 +782,13 @@ impl Deflator { |
|
|
|
|
return Ok(()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
s => panic!(s), |
|
|
|
|
s => panic!("Compression error: {:?}", s), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Inflator { |
|
|
|
|
decompress: Decompress, |
|
|
|
|
} |
|
|
|
@ -719,7 +846,7 @@ impl Inflator { |
|
|
|
|
return Ok(()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
s => panic!(s), |
|
|
|
|
s => panic!("Decompression error: {:?}", s), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|