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}