twist_deflate/
lib.rs

1//! Per-message Deflate Extension
2extern crate slog_term;
3
4#[macro_use]
5extern crate slog;
6#[macro_use]
7extern crate twist;
8
9use slog::Logger;
10use std::io;
11use twist::{BaseFrame, FromHeader, IntoResponse, OpCode, PerMessage};
12
13/// This extension needs to use the `rsv1` bit in a websocket frame.
14const RSV1: u8 = 4;
15/// The `Sec-WebSocket-Extensions` header prefix.
16const SWE: &'static str = "Sec-WebSocket-Extensions: ";
17/// The `permessage-deflate` header value.
18const PMD: &'static str = "permessage-deflate";
19
20/// Generate an `io::ErrorKind::Other` error with the given description.
21fn other(desc: &str) -> io::Error {
22    io::Error::new(io::ErrorKind::Other, desc)
23}
24
25/// The per-message deflate state.
26pub struct Deflate {
27    /// Is the Deflate extension enabled?
28    enabled: bool,
29    /// By including this extension parameter in an extension negotiation offer, a client prevents
30    /// the peer server from using context takeover. Absence of this extension parameter in an
31    /// extension negotiation offer indicates that the client can decompress a message that the
32    /// server built using context takeover.
33    _server_no_context_takeover: bool,
34    /// By including this extension parameter in an extension negotiation offer, a client informs
35    /// the peer server of a hint that even if the server doesn't include the
36    /// `client_no_context_takeover` extension parameter in the corresponding extension negotiation
37    /// response to the offer, the client is not going to use context takeover. By including the
38    /// `client_no_context_takeover` extension parameter in an extension negotiation response, a
39    /// server prevents the peer client from using context takeover. Absence of this extension
40    /// parameter in an extension negotiation response indicates that the server can decompress
41    /// messages built by the client using context takeover.
42    _client_no_context_takeover: bool,
43    /// By including this parameter in an extension negotiation offer, a client limits the LZ77
44    /// sliding window size that the server will use to compress messages. A server declines an
45    /// extension negotiation offer with this parameter if the server doesn't support it.
46    /// Absence of this parameter in an extension negotiation offer indicates that the client can
47    /// receive messages compressed using an LZ77 sliding window of up to 32,768 bytes. A server
48    /// accepts an extension negotiation offer with this parameter by including the
49    /// `server_max_window_bits` extension parameter in the extension negotiation response to send
50    /// back to the client with the same or smaller value as the offer.
51    server_max_window_bits: u8,
52    /// The client max window bits.
53    _client_max_window_bits: u8,
54    /// slog stdout `Logger`
55    stdout: Option<Logger>,
56    /// slog stderr `Logger`
57    stderr: Option<Logger>,
58}
59
60impl Deflate {
61    /// Add a slog stdout `Logger` to this service.
62    pub fn add_stdout(&mut self, stdout: Logger) -> &mut Deflate {
63        let ps_stdout = stdout.new(o!("extension" => "permessage-deflate"));
64        self.stdout = Some(ps_stdout);
65        self
66    }
67
68    /// Add a slog stderr `Logger` to this service.
69    pub fn add_stderr(&mut self, stderr: Logger) -> &mut Deflate {
70        let ps_stderr = stderr.new(o!("extension" => "permessage-deflate"));
71        self.stderr = Some(ps_stderr);
72        self
73    }
74}
75
76impl Default for Deflate {
77    fn default() -> Deflate {
78        Deflate {
79            enabled: false,
80            _server_no_context_takeover: false,
81            _client_no_context_takeover: true,
82            server_max_window_bits: 15,
83            _client_max_window_bits: 15,
84            stdout: None,
85            stderr: None,
86        }
87    }
88}
89
90impl FromHeader for Deflate {
91    fn init(&mut self, request: &str) {
92        try_trace!(self.stdout, "init Deflate with header '{}'", request);
93
94        if request.contains(PMD) {
95            self.enabled = true;
96            try_trace!(self.stdout, "permessage deflate is enabled");
97
98            let blah: Vec<&str> =
99                request.split(',').take_while(|s| s.starts_with("permessage-deflate")).collect();
100            for pmd in blah {
101                try_trace!(self.stdout, "{}", pmd);
102            }
103            self.server_max_window_bits = 8;
104        } else {
105            try_trace!(self.stdout, "permessage deflate is disabled");
106        }
107    }
108}
109
110impl IntoResponse for Deflate {
111    fn response(&self) -> String {
112        let mut resp = String::new();
113        if self.enabled {
114            resp.push_str(SWE);
115            resp.push_str(PMD);
116        }
117        resp
118    }
119}
120
121impl PerMessage for Deflate {
122    fn reserve_rsv(&self, current_rsv: u8) -> Result<u8, io::Error> {
123        if self.enabled {
124            if current_rsv & RSV1 == 0 {
125                Ok(current_rsv | RSV1)
126            } else {
127                Err(other("rsv1 bit already reserved"))
128            }
129        } else {
130            Ok(0)
131        }
132    }
133
134    fn uses_extension_data(&self) -> bool {
135        false
136    }
137
138    fn decode(&self, message: &mut BaseFrame) -> Result<(), io::Error> {
139        if self.enabled {
140            let opcode = message.opcode();
141            if (opcode == OpCode::Text || opcode == OpCode::Binary) && message.fin() {
142                try_trace!(self.stdout, "Decompressing Frame");
143            }
144        }
145        Ok(())
146    }
147
148    fn encode(&self, message: &mut BaseFrame) -> Result<(), io::Error> {
149        if self.enabled {
150            let opcode = message.opcode();
151            if (opcode == OpCode::Text || opcode == OpCode::Binary) && message.fin() {
152                try_trace!(self.stdout, "Compressing Frame");
153            }
154        }
155        Ok(())
156    }
157}
158
159
160#[test]
161fn it_works() {}