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 "Hello" 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