1 extern crate tiny_http;
2 extern crate rustc_serialize;
3 extern crate sha1;
4 
5 use std::thread::spawn;
6 use std::io::Cursor;
7 use std::io::Read;
8 
9 use rustc_serialize::base64::{Config, Standard, ToBase64, Newline};
10 
11 
home_page(port: u16) -> tiny_http::Response<Cursor<Vec<u8>>>12 fn home_page(port: u16) -> tiny_http::Response<Cursor<Vec<u8>>> {
13     tiny_http::Response::from_string(format!("
14         <script type=\"text/javascript\">
15         var socket = new WebSocket(\"ws://localhost:{}/\", \"ping\");
16 
17         function send(data) {{
18             socket.send(data);
19         }}
20 
21         socket.onmessage = function(event) {{
22             document.getElementById('result').innerHTML += event.data + '<br />';
23         }}
24         </script>
25         <p>This example will receive &quot;Hello&quot; for each byte in the packet being sent.
26         Tiny-http doesn't support decoding websocket frames, so we can't do anything better.</p>
27         <p><input type=\"text\" id=\"msg\" />
28         <button onclick=\"send(document.getElementById('msg').value)\">Send</button></p>
29         <p>Received: </p>
30         <p id=\"result\"></p>
31     ", port))
32         .with_header("Content-type: text/html".parse::<tiny_http::Header>().unwrap())
33 }
34 
35 /// Turns a Sec-WebSocket-Key into a Sec-WebSocket-Accept.
36 /// Feel free to copy-paste this function, but please use a better error handling.
convert_key(input: &str) -> String37 fn convert_key(input: &str) -> String {
38     use sha1::Sha1;
39 
40     let mut input = input.to_string().into_bytes();
41     let mut bytes = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".to_string().into_bytes();
42     input.append(&mut bytes);
43 
44     let mut sha1 = Sha1::new();
45     sha1.update(&input);
46 
47     sha1.digest().bytes().to_base64(Config { char_set: Standard, pad: true,
48                                              line_length: None, newline: Newline::LF })
49 }
50 
main()51 fn main() {
52     let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
53     let port = server.server_addr().port();
54 
55     println!("Server started");
56     println!("To try this example, open a browser to http://localhost:{}/", port);
57 
58     for request in server.incoming_requests() {
59         // we are handling this websocket connection in a new task
60         spawn(move || {
61             // checking the "Upgrade" header to check that it is a websocket
62             match request.headers().iter()
63                 .find(|h| h.field.equiv(&"Upgrade"))
64                 .and_then(|hdr| if hdr.value == "websocket" { Some(hdr) } else { None })
65             {
66                 None => {
67                     // sending the HTML page
68                     request.respond(home_page(port));
69                     return
70                 },
71                 _ => ()
72             };
73 
74             // getting the value of Sec-WebSocket-Key
75             let key = match request.headers().iter()
76                 .find(|h| h.field.equiv(&"Sec-WebSocket-Key"))
77                 .map(|h| h.value.clone())
78             {
79                 None => {
80                     let response = tiny_http::Response::new_empty(tiny_http::StatusCode(400));
81                     request.respond(response);
82                     return;
83                 },
84                 Some(k) => k
85             };
86 
87             // building the "101 Switching Protocols" response
88             let response = tiny_http::Response::new_empty(tiny_http::StatusCode(101))
89                 .with_header("Upgrade: websocket".parse::<tiny_http::Header>().unwrap())
90                 .with_header("Connection: Upgrade".parse::<tiny_http::Header>().unwrap())
91                 .with_header("Sec-WebSocket-Protocol: ping".parse::<tiny_http::Header>().unwrap())
92                 .with_header(
93                     format!("Sec-WebSocket-Accept: {}",
94                         convert_key(key.as_str())
95                     ).parse::<tiny_http::Header>().unwrap());
96 
97             //
98             let mut stream = request.upgrade("websocket", response);
99 
100             //
101             loop {
102                 let mut out = Vec::new();
103                 match Read::by_ref(&mut stream).take(1).read_to_end(&mut out) {
104                     Ok(n) if n >= 1 => {
105                         // "Hello" frame
106                         let data = [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f];
107                         stream.write(&data).ok();
108                         stream.flush().ok();
109                     },
110                     Ok(_) => panic!("eof ; should never happen"),
111                     Err(e) => {
112                         println!("closing connection because: {}", e);
113                         return
114                     },
115                 };
116             }
117         });
118     }
119 }
120