2021-02-28 20:55:34 +01:00
|
|
|
//! Sets up a WebSocket server over TCP and TLS.
|
|
|
|
//! Sends a heartbeat message every 4 seconds but does not respond to any incoming frames.
|
|
|
|
|
|
|
|
extern crate tls_rustls as rustls;
|
|
|
|
|
|
|
|
use std::{
|
2021-05-06 21:24:18 +02:00
|
|
|
io,
|
2021-02-28 20:55:34 +01:00
|
|
|
pin::Pin,
|
|
|
|
task::{Context, Poll},
|
|
|
|
time::Duration,
|
|
|
|
};
|
|
|
|
|
|
|
|
use actix_codec::Encoder;
|
2021-04-09 19:07:10 +02:00
|
|
|
use actix_http::{body::BodyStream, error::Error, ws, HttpService, Request, Response};
|
2021-02-28 20:55:34 +01:00
|
|
|
use actix_rt::time::{interval, Interval};
|
|
|
|
use actix_server::Server;
|
|
|
|
use bytes::{Bytes, BytesMut};
|
|
|
|
use bytestring::ByteString;
|
|
|
|
use futures_core::{ready, Stream};
|
|
|
|
|
|
|
|
#[actix_rt::main]
|
|
|
|
async fn main() -> io::Result<()> {
|
2021-05-06 21:24:18 +02:00
|
|
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
2021-02-28 20:55:34 +01:00
|
|
|
|
|
|
|
Server::build()
|
|
|
|
.bind("tcp", ("127.0.0.1", 8080), || {
|
|
|
|
HttpService::build().h1(handler).tcp()
|
|
|
|
})?
|
|
|
|
.bind("tls", ("127.0.0.1", 8443), || {
|
|
|
|
HttpService::build().finish(handler).rustls(tls_config())
|
|
|
|
})?
|
|
|
|
.run()
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2021-04-09 19:07:10 +02:00
|
|
|
async fn handler(req: Request) -> Result<Response<BodyStream<Heartbeat>>, Error> {
|
2021-02-28 20:55:34 +01:00
|
|
|
log::info!("handshaking");
|
|
|
|
let mut res = ws::handshake(req.head())?;
|
|
|
|
|
|
|
|
// handshake will always fail under HTTP/2
|
|
|
|
|
|
|
|
log::info!("responding");
|
2021-05-09 21:12:48 +02:00
|
|
|
Ok(res.message_body(BodyStream::new(Heartbeat::new(ws::Codec::new())))?)
|
2021-02-28 20:55:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Heartbeat {
|
|
|
|
codec: ws::Codec,
|
|
|
|
interval: Interval,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Heartbeat {
|
|
|
|
fn new(codec: ws::Codec) -> Self {
|
|
|
|
Self {
|
|
|
|
codec,
|
|
|
|
interval: interval(Duration::from_secs(4)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Stream for Heartbeat {
|
|
|
|
type Item = Result<Bytes, Error>;
|
|
|
|
|
|
|
|
fn poll_next(
|
|
|
|
mut self: Pin<&mut Self>,
|
|
|
|
cx: &mut Context<'_>,
|
|
|
|
) -> Poll<Option<Self::Item>> {
|
|
|
|
log::trace!("poll");
|
|
|
|
|
|
|
|
ready!(self.as_mut().interval.poll_tick(cx));
|
|
|
|
|
|
|
|
let mut buffer = BytesMut::new();
|
|
|
|
|
|
|
|
self.as_mut()
|
|
|
|
.codec
|
|
|
|
.encode(
|
|
|
|
ws::Message::Text(ByteString::from_static("hello world")),
|
|
|
|
&mut buffer,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Poll::Ready(Some(Ok(buffer.freeze())))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tls_config() -> rustls::ServerConfig {
|
|
|
|
use std::io::BufReader;
|
|
|
|
|
2021-10-20 03:00:11 +02:00
|
|
|
use rustls::{Certificate, PrivateKey};
|
|
|
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
2021-02-28 20:55:34 +01:00
|
|
|
|
|
|
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_owned()]).unwrap();
|
|
|
|
let cert_file = cert.serialize_pem().unwrap();
|
|
|
|
let key_file = cert.serialize_private_key_pem();
|
|
|
|
|
|
|
|
let cert_file = &mut BufReader::new(cert_file.as_bytes());
|
|
|
|
let key_file = &mut BufReader::new(key_file.as_bytes());
|
|
|
|
|
2021-10-20 03:00:11 +02:00
|
|
|
let cert_chain = certs(cert_file)
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.map(Certificate)
|
|
|
|
.collect();
|
2021-02-28 20:55:34 +01:00
|
|
|
let mut keys = pkcs8_private_keys(key_file).unwrap();
|
2021-10-20 03:00:11 +02:00
|
|
|
|
|
|
|
let mut config = rustls::ServerConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_no_client_auth()
|
|
|
|
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
config.alpn_protocols.push(b"http/1.1".to_vec());
|
|
|
|
config.alpn_protocols.push(b"h2".to_vec());
|
2021-02-28 20:55:34 +01:00
|
|
|
|
|
|
|
config
|
|
|
|
}
|