hawk_ws/
handler.rs

1use log::LogLevel::Error as ErrorLevel;
2#[cfg(feature = "ssl")]
3use openssl::ssl::{SslConnectorBuilder, SslMethod, SslStream};
4use url;
5
6use frame::Frame;
7use handshake::{Handshake, Request, Response};
8use message::Message;
9use protocol::CloseCode;
10use result::{Error, Kind, Result};
11use util::{Timeout, Token};
12
13#[cfg(feature = "ssl")]
14use util::TcpStream;
15
16/// The core trait of this library.
17/// Implementing this trait provides the business logic of the WebSocket application.
18pub trait Handler {
19    // general
20
21    /// Called when a request to shutdown all connections has been received.
22    #[inline]
23    fn on_shutdown(&mut self) {
24        debug!("Handler received WebSocket shutdown request.");
25    }
26
27    // WebSocket events
28
29    /// Called when the WebSocket handshake is successful and the connection is open for sending
30    /// and receiving messages.
31    fn on_open(&mut self, shake: Handshake) -> Result<()> {
32        if let Some(addr) = try!(shake.remote_addr()) {
33            debug!("Connection with {} now open", addr);
34        }
35        Ok(())
36    }
37
38    /// Called on incoming messages.
39    fn on_message(&mut self, msg: Message) -> Result<()> {
40        debug!("Received message {:?}", msg);
41        Ok(())
42    }
43
44    /// Called any time this endpoint receives a close control frame.
45    /// This may be because the other endpoint is initiating a closing handshake,
46    /// or it may be the other endpoint confirming the handshake initiated by this endpoint.
47    fn on_close(&mut self, code: CloseCode, reason: &str) {
48        debug!("Connection closing due to ({:?}) {}", code, reason);
49    }
50
51    /// Called when an error occurs on the WebSocket.
52    fn on_error(&mut self, err: Error) {
53        // Ignore connection reset errors by default, but allow library clients to see them by
54        // overriding this method if they want
55        if let Kind::Io(ref err) = err.kind {
56            if let Some(104) = err.raw_os_error() {
57                return;
58            }
59        }
60
61        error!("{:?}", err);
62        if !log_enabled!(ErrorLevel) {
63            println!(
64                "Encountered an error: {}\nEnable a logger to see more information.",
65                err
66            );
67        }
68    }
69
70    // handshake events
71
72    /// A method for handling the low-level workings of the request portion of the WebSocket
73    /// handshake.
74    ///
75    /// Implementors should select a WebSocket protocol and extensions where they are supported.
76    ///
77    /// Implementors can inspect the Request and must return a Response or an error
78    /// indicating that the handshake failed. The default implementation provides conformance with
79    /// the WebSocket protocol, and implementors should use the `Response::from_request` method and
80    /// then modify the resulting response as necessary in order to maintain conformance.
81    ///
82    /// This method will not be called when the handler represents a client endpoint. Use
83    /// `build_request` to provide an initial handshake request.
84    ///
85    /// # Examples
86    ///
87    /// ```ignore
88    /// let mut res = try!(Response::from_request(req));
89    /// if try!(req.extensions()).iter().find(|&&ext| ext.contains("myextension-name")).is_some() {
90    ///     res.add_extension("myextension-name")
91    /// }
92    /// Ok(res)
93    /// ```
94    #[inline]
95    fn on_request(&mut self, req: &Request) -> Result<Response> {
96        debug!("Handler received request:\n{}", req);
97        Response::from_request(req)
98    }
99
100    /// A method for handling the low-level workings of the response portion of the WebSocket
101    /// handshake.
102    ///
103    /// Implementors can inspect the Response and choose to fail the connection by
104    /// returning an error. This method will not be called when the handler represents a server
105    /// endpoint. The response should indicate which WebSocket protocol and extensions the server
106    /// has agreed to if any.
107    #[inline]
108    fn on_response(&mut self, res: &Response) -> Result<()> {
109        debug!("Handler received response:\n{}", res);
110        Ok(())
111    }
112
113    // timeout events
114
115    /// Called when a timeout is triggered.
116    ///
117    /// This method will be called when the eventloop encounters a timeout on the specified
118    /// token. To schedule a timeout with your specific token use the `Sender::timeout` method.
119    ///
120    /// # Examples
121    ///
122    /// ```ignore
123    /// const GRATI: Token = Token(1);
124    ///
125    /// ... Handler
126    ///
127    /// fn on_open(&mut self, _: Handshake) -> Result<()> {
128    ///     // schedule a timeout to send a gratuitous pong every 5 seconds
129    ///     self.ws.timeout(5_000, GRATI)
130    /// }
131    ///
132    /// fn on_timeout(&mut self, event: Token) -> Result<()> {
133    ///     if event == GRATI {
134    ///         // send gratuitous pong
135    ///         try!(self.ws.pong(vec![]))
136    ///         // reschedule the timeout
137    ///         self.ws.timeout(5_000, GRATI)
138    ///     } else {
139    ///         Err(Error::new(ErrorKind::Internal, "Invalid timeout token encountered!"))
140    ///     }
141    /// }
142    /// ```
143    #[inline]
144    fn on_timeout(&mut self, event: Token) -> Result<()> {
145        debug!("Handler received timeout token: {:?}", event);
146        Ok(())
147    }
148
149    /// Called when a timeout has been scheduled on the eventloop.
150    ///
151    /// This method is the hook for obtaining a Timeout object that may be used to cancel a
152    /// timeout. This is a noop by default.
153    ///
154    /// # Examples
155    ///
156    /// ```ignore
157    /// const PING: Token = Token(1);
158    /// const EXPIRE: Token = Token(2);
159    ///
160    /// ... Handler
161    ///
162    /// fn on_open(&mut self, _: Handshake) -> Result<()> {
163    ///     // schedule a timeout to send a ping every 5 seconds
164    ///     try!(self.ws.timeout(5_000, PING));
165    ///     // schedule a timeout to close the connection if there is no activity for 30 seconds
166    ///     self.ws.timeout(30_000, EXPIRE)
167    /// }
168    ///
169    /// fn on_timeout(&mut self, event: Token) -> Result<()> {
170    ///     match event {
171    ///         PING => {
172    ///             self.ws.ping(vec![]);
173    ///             self.ws.timeout(5_000, PING)
174    ///         }
175    ///         EXPIRE => self.ws.close(CloseCode::Away),
176    ///         _ => Err(Error::new(ErrorKind::Internal, "Invalid timeout token encountered!")),
177    ///     }
178    /// }
179    ///
180    /// fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> {
181    ///     if event == EXPIRE {
182    ///         if let Some(t) = self.timeout.take() {
183    ///             try!(self.ws.cancel(t))
184    ///         }
185    ///         self.timeout = Some(timeout)
186    ///     }
187    ///     Ok(())
188    /// }
189    ///
190    /// fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
191    ///     // some activity has occurred, let's reset the expiration
192    ///     try!(self.ws.timeout(30_000, EXPIRE));
193    ///     Ok(Some(frame))
194    /// }
195    /// ```
196    #[inline]
197    fn on_new_timeout(&mut self, _: Token, _: Timeout) -> Result<()> {
198        // default implementation discards the timeout handle
199        Ok(())
200    }
201
202    // frame events
203
204    /// A method for handling incoming frames.
205    ///
206    /// This method provides very low-level access to the details of the WebSocket protocol. It may
207    /// be necessary to implement this method in order to provide a particular extension, but
208    /// incorrect implementation may cause the other endpoint to fail the connection.
209    ///
210    /// Returning `Ok(None)` will cause the connection to forget about a particular frame. This is
211    /// useful if you want ot filter out a frame or if you don't want any of the default handler
212    /// methods to run.
213    ///
214    /// By default this method simply ensures that no reserved bits are set.
215    #[inline]
216    fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
217        debug!("Handler received: {}", frame);
218        // default implementation doesn't allow for reserved bits to be set
219        if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
220            Err(Error::new(
221                Kind::Protocol,
222                "Encountered frame with reserved bits set.",
223            ))
224        } else {
225            Ok(Some(frame))
226        }
227    }
228
229    /// A method for handling outgoing frames.
230    ///
231    /// This method provides very low-level access to the details of the WebSocket protocol. It may
232    /// be necessary to implement this method in order to provide a particular extension, but
233    /// incorrect implementation may cause the other endpoint to fail the connection.
234    ///
235    /// Returning `Ok(None)` will cause the connection to forget about a particular frame, meaning
236    /// that it will not be sent. You can use this approach to merge multiple frames into a single
237    /// frame before sending the message.
238    ///
239    /// For messages, this method will be called with a single complete, final frame before any
240    /// fragmentation is performed. Automatic fragmentation will be performed on the returned
241    /// frame, if any, based on the `fragment_size` setting.
242    ///
243    /// By default this method simply ensures that no reserved bits are set.
244    #[inline]
245    fn on_send_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
246        trace!("Handler will send: {}", frame);
247        // default implementation doesn't allow for reserved bits to be set
248        if frame.has_rsv1() || frame.has_rsv2() || frame.has_rsv3() {
249            Err(Error::new(
250                Kind::Protocol,
251                "Encountered frame with reserved bits set.",
252            ))
253        } else {
254            Ok(Some(frame))
255        }
256    }
257
258    // constructors
259
260    /// A method for creating the initial handshake request for WebSocket clients.
261    ///
262    /// The default implementation provides conformance with the WebSocket protocol, but this
263    /// method may be overridden. In order to facilitate conformance,
264    /// implementors should use the `Request::from_url` method and then modify the resulting
265    /// request as necessary.
266    ///
267    /// Implementors should indicate any available WebSocket extensions here.
268    ///
269    /// # Examples
270    /// ```ignore
271    /// let mut req = try!(Request::from_url(url));
272    /// req.add_extension("permessage-deflate; client_max_window_bits");
273    /// Ok(req)
274    /// ```
275    #[inline]
276    fn build_request(&mut self, url: &url::Url) -> Result<Request> {
277        trace!("Handler is building request to {}.", url);
278        Request::from_url(url)
279    }
280
281    /// A method for wrapping a client TcpStream with Ssl Authentication machinery
282    ///
283    /// Override this method to customize how the connection is encrypted. By default
284    /// this will use the Server Name Indication extension in conformance with RFC6455.
285    #[inline]
286    #[cfg(feature = "ssl")]
287    fn upgrade_ssl_client(
288        &mut self,
289        stream: TcpStream,
290        url: &url::Url,
291    ) -> Result<SslStream<TcpStream>> {
292        let domain = try!(url.domain().ok_or(Error::new(
293            Kind::Protocol,
294            format!("Unable to parse domain from {}. Needed for SSL.", url)
295        )));
296        let connector = try!(SslConnectorBuilder::new(SslMethod::tls()).map_err(|e| {
297            Error::new(
298                Kind::Internal,
299                format!("Failed to upgrade client to SSL: {}", e),
300            )
301        }))
302        .build();
303        connector.connect(domain, stream).map_err(Error::from)
304    }
305
306    /// A method for wrapping a server TcpStream with Ssl Authentication machinery
307    ///
308    /// Override this method to customize how the connection is encrypted. By default
309    /// this method is not implemented.
310    #[inline]
311    #[cfg(feature = "ssl")]
312    fn upgrade_ssl_server(&mut self, _: TcpStream) -> Result<SslStream<TcpStream>> {
313        unimplemented!()
314    }
315}
316
317impl<F> Handler for F
318where
319    F: Fn(Message) -> Result<()>,
320{
321    fn on_message(&mut self, msg: Message) -> Result<()> {
322        self(msg)
323    }
324}
325
326mod test {
327    #![allow(unused_imports, unused_variables, dead_code)]
328    use super::*;
329    use frame;
330    use handshake::{Handshake, Request, Response};
331    use message;
332    use mio;
333    use protocol::CloseCode;
334    use result::Result;
335    use url;
336
337    #[derive(Debug, Eq, PartialEq)]
338    struct M;
339    impl Handler for M {
340        fn on_message(&mut self, _: message::Message) -> Result<()> {
341            Ok(println!("test"))
342        }
343
344        fn on_frame(&mut self, f: frame::Frame) -> Result<Option<frame::Frame>> {
345            Ok(None)
346        }
347    }
348
349    #[test]
350    fn handler() {
351        struct H;
352
353        impl Handler for H {
354            fn on_open(&mut self, shake: Handshake) -> Result<()> {
355                assert!(shake.request.key().is_ok());
356                assert!(shake.response.key().is_ok());
357                Ok(())
358            }
359
360            fn on_message(&mut self, msg: message::Message) -> Result<()> {
361                Ok(assert_eq!(
362                    msg,
363                    message::Message::Text(String::from("testme"))
364                ))
365            }
366
367            fn on_close(&mut self, code: CloseCode, _: &str) {
368                assert_eq!(code, CloseCode::Normal)
369            }
370        }
371
372        let mut h = H;
373        let url = url::Url::parse("wss://127.0.0.1:3012").unwrap();
374        let req = Request::from_url(&url).unwrap();
375        let res = Response::from_request(&req).unwrap();
376        h.on_open(Handshake {
377            request: req,
378            response: res,
379            peer_addr: None,
380            local_addr: None,
381        })
382        .unwrap();
383        h.on_message(message::Message::Text("testme".to_owned()))
384            .unwrap();
385        h.on_close(CloseCode::Normal, "");
386    }
387
388    #[test]
389    fn closure_handler() {
390        let mut close = |msg| {
391            assert_eq!(msg, message::Message::Binary(vec![1, 2, 3]));
392            Ok(())
393        };
394
395        close
396            .on_message(message::Message::Binary(vec![1, 2, 3]))
397            .unwrap();
398    }
399}