soketto/
extension.rs

1// Copyright (c) 2019 Parity Technologies (UK) Ltd.
2// Copyright (c) 2016 twist developers
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10//! Websocket extensions as per [RFC 6455][rfc6455].
11//!
12//! [rfc6455]: https://tools.ietf.org/html/rfc6455#section-9
13
14#[cfg(feature = "deflate")]
15pub mod deflate;
16
17use crate::{base::Header, BoxedError, Storage};
18use std::{borrow::Cow, fmt};
19
20/// A websocket extension as per RFC 6455, section 9.
21///
22/// Extensions are invoked during handshake and subsequently during base
23/// frame encoding and decoding. The invocation during handshake differs
24/// on client and server side.
25///
26/// # Server
27///
28/// 1. All extensions should consider themselves as disabled but available.
29/// 2. When receiving a handshake request from a client, for each extension
30/// with a matching name, [`Extension::configure`] will be applied to the
31/// request parameters. The extension may internally enable itself.
32/// 3. When sending back the response, for each extension whose
33/// [`Extension::is_enabled`] returns true, the extension name and its
34/// parameters (as returned by [`Extension::params`]) will be included in the
35/// response.
36///
37/// # Client
38///
39/// 1. All extensions should consider themselves as disabled but available.
40/// 2. When creating the handshake request, all extensions and its parameters
41/// (as returned by [`Extension::params`]) will be included in the request.
42/// 3. When receiving the response from the server, for every extension with
43/// a matching name in the response, [`Extension::configure`] will be applied
44/// to the response parameters. The extension may internally enable itself.
45///
46/// After this handshake phase, extensions have been configured and are
47/// potentially enabled. Enabled extensions can then be used for further base
48/// frame processing.
49pub trait Extension: std::fmt::Debug {
50	/// Is this extension enabled?
51	fn is_enabled(&self) -> bool;
52
53	/// The name of this extension.
54	fn name(&self) -> &str;
55
56	/// The parameters this extension wants to send for negotiation.
57	fn params(&self) -> &[Param];
58
59	/// Configure this extension with the parameters received from negotiation.
60	fn configure(&mut self, params: &[Param]) -> Result<(), BoxedError>;
61
62	/// Encode a frame, given as frame header and payload data.
63	fn encode(&mut self, header: &mut Header, data: &mut Storage) -> Result<(), BoxedError>;
64
65	/// Decode a frame.
66	///
67	/// The frame header is given, as well as the accumulated payload data, i.e.
68	/// the concatenated payload data of all message fragments.
69	fn decode(&mut self, header: &mut Header, data: &mut Vec<u8>) -> Result<(), BoxedError>;
70
71	/// The reserved bits this extension uses.
72	fn reserved_bits(&self) -> (bool, bool, bool) {
73		(false, false, false)
74	}
75}
76
77impl<E: Extension + ?Sized> Extension for Box<E> {
78	fn is_enabled(&self) -> bool {
79		(**self).is_enabled()
80	}
81
82	fn name(&self) -> &str {
83		(**self).name()
84	}
85
86	fn params(&self) -> &[Param] {
87		(**self).params()
88	}
89
90	fn configure(&mut self, params: &[Param]) -> Result<(), BoxedError> {
91		(**self).configure(params)
92	}
93
94	fn encode(&mut self, header: &mut Header, data: &mut Storage) -> Result<(), BoxedError> {
95		(**self).encode(header, data)
96	}
97
98	fn decode(&mut self, header: &mut Header, data: &mut Vec<u8>) -> Result<(), BoxedError> {
99		(**self).decode(header, data)
100	}
101
102	fn reserved_bits(&self) -> (bool, bool, bool) {
103		(**self).reserved_bits()
104	}
105}
106
107/// Extension parameter (used for negotiation).
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct Param<'a> {
110	name: Cow<'a, str>,
111	value: Option<Cow<'a, str>>,
112}
113
114impl<'a> fmt::Display for Param<'a> {
115	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116		if let Some(v) = &self.value {
117			write!(f, "{} = {}", self.name, v)
118		} else {
119			write!(f, "{}", self.name)
120		}
121	}
122}
123
124impl<'a> Param<'a> {
125	/// Create a new parameter with the given name.
126	pub fn new(name: impl Into<Cow<'a, str>>) -> Self {
127		Param { name: name.into(), value: None }
128	}
129
130	/// Access the parameter name.
131	pub fn name(&self) -> &str {
132		&self.name
133	}
134
135	/// Access the optional parameter value.
136	pub fn value(&self) -> Option<&str> {
137		self.value.as_ref().map(|v| v.as_ref())
138	}
139
140	/// Set the parameter to the given value.
141	pub fn set_value(&mut self, value: Option<impl Into<Cow<'a, str>>>) -> &mut Self {
142		self.value = value.map(Into::into);
143		self
144	}
145
146	/// Turn this parameter into one that owns its values.
147	pub fn acquire(self) -> Param<'static> {
148		Param { name: Cow::Owned(self.name.into_owned()), value: self.value.map(|v| Cow::Owned(v.into_owned())) }
149	}
150}