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