Add extension negotiation methods to `WebSocketConfig`

pull/235/head
kazk 4 years ago
parent 40eb9235d9
commit 8dc8a413cb
  1. 11
      src/extensions/compression/deflate.rs
  2. 5
      src/handshake/client.rs
  3. 4
      src/handshake/server.rs
  4. 34
      src/protocol/mod.rs

@ -4,7 +4,7 @@ use flate2::{Compress, Compression, Decompress, FlushCompress, FlushDecompress,
use http::HeaderValue; use http::HeaderValue;
use thiserror::Error; use thiserror::Error;
use crate::{extensions, protocol::Role}; use crate::protocol::Role;
const PER_MESSAGE_DEFLATE: &str = "permessage-deflate"; const PER_MESSAGE_DEFLATE: &str = "permessage-deflate";
const CLIENT_NO_CONTEXT_TAKEOVER: &str = "client_no_context_takeover"; const CLIENT_NO_CONTEXT_TAKEOVER: &str = "client_no_context_takeover";
@ -67,11 +67,10 @@ impl DeflateConfig {
to_header_value(&offers) to_header_value(&offers)
} }
// This can be used for `WebSocket::from_raw_socket_with_compression`.
/// Returns negotiation response based on offers and `DeflateContext` to manage per message compression. /// Returns negotiation response based on offers and `DeflateContext` to manage per message compression.
pub fn negotiation_response<'a>( pub(crate) fn accept_offer<'a>(
&'a self, &'a self,
extensions: impl Iterator<Item = &'a HeaderValue>, offers: impl Iterator<Item = impl Iterator<Item = (&'a str, Option<&'a str>)>>,
) -> Option<(HeaderValue, DeflateContext)> { ) -> Option<(HeaderValue, DeflateContext)> {
// Accept the first valid offer for `permessage-deflate`. // Accept the first valid offer for `permessage-deflate`.
// A server MUST decline an extension negotiation offer for this // A server MUST decline an extension negotiation offer for this
@ -83,9 +82,7 @@ impl DeflateConfig {
// * The negotiation offer contains multiple extension parameters with // * The negotiation offer contains multiple extension parameters with
// the same name. // the same name.
// * The server doesn't support the offered configuration. // * The server doesn't support the offered configuration.
'outer: for (_, offer) in 'outer: for offer in offers {
extensions::iter_all(extensions).filter(|&(k, _)| k == self.name())
{
let mut config = let mut config =
DeflateConfig { compression: self.compression, ..DeflateConfig::default() }; DeflateConfig { compression: self.compression, ..DeflateConfig::default() };
let mut agreed = Vec::new(); let mut agreed = Vec::new();

@ -141,9 +141,8 @@ fn generate_request(
} }
writeln!(req, "{}: {}\r", k, v.to_str()?).unwrap(); writeln!(req, "{}: {}\r", k, v.to_str()?).unwrap();
} }
if let Some(compression) = &config.and_then(|c| c.compression) { if let Some(offers) = config.and_then(|c| c.generate_offers()) {
let offer = compression.generate_offer(); writeln!(req, "Sec-WebSocket-Extensions: {}\r", offers.to_str()?).unwrap();
writeln!(req, "Sec-WebSocket-Extensions: {}\r", offer.to_str()?).unwrap();
} }
writeln!(req, "\r").unwrap(); writeln!(req, "\r").unwrap();
trace!("Request: {:?}", String::from_utf8_lossy(&req)); trace!("Request: {:?}", String::from_utf8_lossy(&req));

@ -245,9 +245,9 @@ impl<S: Read + Write, C: Callback> HandshakeRole for ServerHandshake<S, C> {
} }
let mut response = create_response(&result)?; let mut response = create_response(&result)?;
if let Some(compression) = &self.config.and_then(|c| c.compression) { if let Some(config) = &self.config {
let extensions = result.headers().get_all("Sec-WebSocket-Extensions").iter(); let extensions = result.headers().get_all("Sec-WebSocket-Extensions").iter();
if let Some((agreed, pmce)) = compression.negotiation_response(extensions) { if let Some((agreed, pmce)) = config.accept_offers(extensions) {
self.pmce = Some(pmce); self.pmce = Some(pmce);
response.headers_mut().insert("Sec-WebSocket-Extensions", agreed); response.headers_mut().insert("Sec-WebSocket-Extensions", agreed);
} }

@ -6,6 +6,7 @@ mod message;
pub use self::{frame::CloseFrame, message::Message}; pub use self::{frame::CloseFrame, message::Message};
use http::HeaderValue;
use log::*; use log::*;
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
@ -73,6 +74,39 @@ impl Default for WebSocketConfig {
} }
} }
impl WebSocketConfig {
// Generate extension negotiation offers for configured extensions.
// Only `permessage-deflate` is supported at the moment.
pub(crate) fn generate_offers(&self) -> Option<HeaderValue> {
self.compression.map(|c| c.generate_offer())
}
// TODO Replace `DeflateContext` with something more general
// This can be used with `WebSocket::from_raw_socket_with_compression` for integration.
/// Returns negotiation response based on offers and `DeflateContext` to manage per message compression.
pub fn accept_offers<'a>(
&'a self,
extensions: impl Iterator<Item = &'a HeaderValue>,
) -> Option<(HeaderValue, DeflateContext)> {
if let Some(compression) = &self.compression {
let extensions = crate::extensions::iter_all(extensions);
let offers =
extensions.filter_map(
|(k, v)| {
if k == compression.name() {
Some(v)
} else {
None
}
},
);
compression.accept_offer(offers)
} else {
None
}
}
}
/// WebSocket input-output stream. /// WebSocket input-output stream.
/// ///
/// This is THE structure you want to create to be able to speak the WebSocket protocol. /// This is THE structure you want to create to be able to speak the WebSocket protocol.

Loading…
Cancel
Save