Last active
February 16, 2018 02:42
-
-
Save walfie/ceef93b10e80d895b390aae2d0223b82 to your computer and use it in GitHub Desktop.
h2 with tokio-rustls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "http2-example" | |
version = "0.1.0" | |
authors = ["Walfie"] | |
[dependencies] | |
futures = "0.1.18" | |
h2 = "0.1.0" | |
http = "0.1.4" | |
rustls = "0.12.0" | |
tokio = "0.1.1" | |
tokio-rustls = "0.5.0" | |
tokio-timer = "0.1.2" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title></title> | |
</head> | |
<body> | |
<ul id="items"></ul> | |
<script> | |
// User needs to go to `https://localhost:9000` and dismiss the security | |
// warning first before this will work | |
var url = 'https://localhost:9000/'; | |
var items = document.getElementById('items'); | |
fetch(url).then(function(response) { | |
var next = function(reader) { | |
return reader.read().then(function(result) { | |
var li = document.createElement('li'); | |
if (result.done) { | |
li.innerHTML = 'done'; | |
items.appendChild(li); | |
return; | |
} | |
// retrieve the multi-byte chunk of data | |
var chunk = result.value; | |
var encodedString = String.fromCharCode.apply(null, chunk); | |
var decodedString = decodeURIComponent(escape(encodedString)); | |
li.innerHTML = decodedString; | |
items.appendChild(li); | |
return next(reader); | |
}); | |
}; | |
return next(response.body.getReader()); | |
}).catch(function(e) { | |
var li = document.createElement('li'); | |
li.innerHTML = e; | |
items.appendChild(li); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Based on https://github.com/quininer/tokio-rustls/blob/master/examples/server.rs | |
/// | |
/// ```bash | |
/// openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem | |
/// CERT_FILE=./certificate.pem KEY_FILE=./key.pem cargo run | |
/// curl --insecure https://localhost:9000 | |
/// ``` | |
extern crate futures; | |
extern crate h2; | |
extern crate http; | |
extern crate rustls; | |
extern crate tokio; | |
extern crate tokio_rustls; | |
extern crate tokio_timer; | |
use futures::{Future, Stream}; | |
use h2::server; | |
use http::{Response, StatusCode}; | |
use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; | |
use rustls::internal::pemfile::{certs, rsa_private_keys, pkcs8_private_keys}; | |
use std::fs::File; | |
use std::io::BufReader; | |
use std::time::Duration; | |
use tokio::executor::current_thread; | |
use tokio::net::TcpListener; | |
use tokio_rustls::ServerConfigExt; | |
use tokio_timer::Timer; | |
fn load_file(path: &str) -> BufReader<File> { | |
BufReader::new(File::open(path).expect(&format!("Could not load file: {}", path))) | |
} | |
fn load_certs(path: &str) -> Vec<Certificate> { | |
certs(&mut load_file(path)).expect("failed to load certs") | |
} | |
fn load_private_key(path: &str) -> PrivateKey { | |
let pkcs8 = pkcs8_private_keys(&mut load_file(path)).expect("failed to load pkcs8 keys"); | |
let rsa = rsa_private_keys(&mut load_file(path)).expect("failed to load rsa keys"); | |
if !pkcs8.is_empty() { | |
pkcs8[0].clone() | |
} else { | |
assert!(!rsa.is_empty()); | |
rsa[0].clone() | |
} | |
} | |
fn main() { | |
let addr = "127.0.0.1:9000".parse().unwrap(); | |
let listener = TcpListener::bind(&addr).unwrap(); | |
let cert_file = std::env::var("CERT_FILE").expect("CERT_FILE undefined"); | |
let key_file = std::env::var("KEY_FILE").expect("KEY_FILE undefined"); | |
let tls_config = std::sync::Arc::new({ | |
let mut config = ServerConfig::new(NoClientAuth::new()); | |
config.set_single_cert(load_certs(&cert_file), load_private_key(&key_file)); | |
config.alpn_protocols.push("h2".to_owned()); | |
config | |
}); | |
let worker = listener | |
.incoming() | |
.for_each(move |socket| { | |
println!("Accepted connection: {:?}", socket); | |
let connection = tls_config | |
.accept_async(socket) | |
.map_err(|e| eprintln!("Error: {:?}", e)) | |
.and_then(|stream| { | |
// Start the HTTP/2.0 connection handshake | |
server::handshake(stream) | |
.and_then(|h2| { | |
// Accept all inbound HTTP/2.0 streams sent over the | |
// connection. | |
h2.for_each(|(request, mut respond)| { | |
println!("Received request: {:?}", request); | |
// Build a response with no body | |
let response = Response::builder() | |
.header("Access-Control-Allow-Origin", "*") | |
.status(StatusCode::OK) | |
.body(()) | |
.unwrap(); | |
// Send the response back to the client | |
let mut send_stream = | |
respond.send_response(response, false).unwrap(); | |
let mut count = 0; | |
let body = Timer::default() | |
.interval(Duration::from_secs(1)) | |
.map_err(|_| ()) | |
.for_each(move |_| { | |
let bytes = format!("{}\n", count).as_bytes().into(); | |
count += 1; | |
send_stream | |
.send_data(bytes, false) | |
.map_err(|e| eprintln!("Error sending data: {:?}", e)) | |
}); | |
current_thread::spawn(body); | |
Ok(()) | |
}) | |
}) | |
.map_err(|e| eprintln!("Error: {:?}", e)) | |
}); | |
// Spawn a new task to process each connection. | |
current_thread::spawn(connection); | |
Ok(()) | |
}) | |
.map_err(|e| eprintln!("Error: {:?}", e)); | |
current_thread::run(|_| { | |
println!("Listening on {}", addr); | |
current_thread::spawn(worker); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment