aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2019-11-15 12:53:58 -0500
committerJesse Luehrs <doy@tozt.net>2019-11-15 12:53:58 -0500
commitfe4fa53dbbb6030beae2094e33d1db008532ae3c (patch)
tree645c4b0311ac6d0406c10c54a5daa9279ed588d0
parent69f9306c874f52ecee1f083b0a1075d3f4e175f4 (diff)
downloadteleterm-fe4fa53dbbb6030beae2094e33d1db008532ae3c.tar.gz
teleterm-fe4fa53dbbb6030beae2094e33d1db008532ae3c.zip
add basic websocket server implementation
based on example in the gotham repo - ugly, but it's what is supported for now
-rw-r--r--Cargo.lock161
-rw-r--r--Cargo.toml7
-rw-r--r--src/web.rs78
-rw-r--r--src/web/ws.rs61
4 files changed, 295 insertions, 12 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c2f01d2..40fbea6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -103,6 +103,12 @@ dependencies = [
]
[[package]]
+name = "base64"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
+
+[[package]]
name = "bincode"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -137,7 +143,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
dependencies = [
"arrayref",
- "byte-tools",
+ "byte-tools 0.2.0",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+dependencies = [
+ "block-padding",
+ "byte-tools 0.3.1",
+ "byteorder",
+ "generic-array 0.12.3",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
+dependencies = [
+ "byte-tools 0.3.1",
]
[[package]]
@@ -156,6 +183,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
[[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+
+[[package]]
name = "bytecount"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -435,7 +468,16 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
dependencies = [
- "generic-array",
+ "generic-array 0.9.0",
+]
+
+[[package]]
+name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+dependencies = [
+ "generic-array 0.12.3",
]
[[package]]
@@ -660,6 +702,15 @@ dependencies = [
]
[[package]]
+name = "generic-array"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
name = "getrandom"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -678,9 +729,8 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
[[package]]
name = "gotham"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb734995f768833f633d6acaee1fe768e1395dafc75362a2dac404ba32d7883a"
+version = "0.5.0-dev"
+source = "git+https://github.com/gotham-rs/gotham?rev=d2395926b93710832f8d72b49c9bd3e77516e386#d2395926b93710832f8d72b49c9bd3e77516e386"
dependencies = [
"base64 0.10.1",
"bincode",
@@ -789,9 +839,9 @@ dependencies = [
[[package]]
name = "hyper"
-version = "0.12.35"
+version = "0.12.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6"
+checksum = "7cb44cbce9d8ee4fb36e4c0ad7b794ac44ebaad924b9c8291a63215bb44c2c8f"
dependencies = [
"bytes",
"futures",
@@ -868,6 +918,15 @@ dependencies = [
]
[[package]]
+name = "input_buffer"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1b822cc844905551931d6f81608ed5f50a79c1078a4e2b4d42dbc7c1eedfbf"
+dependencies = [
+ "bytes",
+]
+
+[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1123,6 +1182,12 @@ dependencies = [
]
[[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+
+[[package]]
name = "open"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1719,14 +1784,32 @@ dependencies = [
]
[[package]]
+name = "sha-1"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
+dependencies = [
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
+ "fake-simd",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sha1"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+
+[[package]]
name = "sha2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
dependencies = [
- "block-buffer",
- "byte-tools",
- "digest",
+ "block-buffer 0.3.3",
+ "byte-tools 0.2.0",
+ "digest 0.7.6",
"fake-simd",
]
@@ -1869,6 +1952,7 @@ dependencies = [
name = "teleterm"
version = "0.2.0"
dependencies = [
+ "base64 0.11.0",
"bytes",
"clap",
"component-future",
@@ -1878,6 +1962,7 @@ dependencies = [
"env_logger",
"futures",
"gotham",
+ "hyper",
"lazy_static",
"log",
"mio",
@@ -1889,12 +1974,14 @@ dependencies = [
"regex",
"reqwest",
"serde",
+ "sha1",
"snafu",
"tokio",
"tokio-pty-process-stream",
"tokio-signal",
"tokio-terminal-resize",
"tokio-tls",
+ "tokio-tungstenite",
"ttyrec",
"twoway",
"url 2.1.0",
@@ -2034,6 +2121,18 @@ dependencies = [
]
[[package]]
+name = "tokio-dns-unofficial"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb9bf62ca2c53bf2f2faec3e48a98b6d8c9577c27011cb0203a4beacdc8ab328"
+dependencies = [
+ "futures",
+ "futures-cpupool",
+ "lazy_static",
+ "tokio",
+]
+
+[[package]]
name = "tokio-executor"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2221,6 +2320,22 @@ dependencies = [
]
[[package]]
+name = "tokio-tungstenite"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f95da5281a1a52e72fa3657e571279bcc2b163ba2897ed8eaa34ef97f24fda"
+dependencies = [
+ "bytes",
+ "futures",
+ "native-tls",
+ "tokio-dns-unofficial",
+ "tokio-io",
+ "tokio-tcp",
+ "tokio-tls",
+ "tungstenite",
+]
+
+[[package]]
name = "tokio-udp"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2289,6 +2404,26 @@ dependencies = [
]
[[package]]
+name = "tungstenite"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "577caf571708961603baf59d2e148d12931e0da2e4bb6c5b471dd4a524fef3aa"
+dependencies = [
+ "base64 0.10.1",
+ "byteorder",
+ "bytes",
+ "http",
+ "httparse",
+ "input_buffer",
+ "log",
+ "native-tls",
+ "rand 0.6.5",
+ "sha-1",
+ "url 2.1.0",
+ "utf-8",
+]
+
+[[package]]
name = "twoway"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2393,6 +2528,12 @@ dependencies = [
]
[[package]]
+name = "utf-8"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
+
+[[package]]
name = "utf8parse"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index ea350b8..f1e0ed0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ categories = ["command-line-utilities"]
license = "MIT"
[dependencies]
+base64 = "0.11"
bytes = "0.4"
clap = { version = "2", features = ["wrap_help"] }
component-future = "0.1"
@@ -20,7 +21,9 @@ crossterm = { version = "0.13", features = ["terminal", "input", "screen"], defa
directories = "2"
env_logger = "0.7"
futures = "0.1.29"
-gotham = "0.4"
+# for websocket support - should be able to go back to released version in 0.5
+gotham = { git = "https://github.com/gotham-rs/gotham", rev = "d2395926b93710832f8d72b49c9bd3e77516e386" }
+hyper = "0.12"
lazy_static = "1"
log = { version = "0.4", features = ["release_max_level_info"] }
mio = "0.6.19"
@@ -32,12 +35,14 @@ ratelimit_meter = "5"
regex = { version = "1", features = ["std", "perf"], default_features = false }
reqwest = "0.9.22"
serde = "1"
+sha1 = "0.6"
snafu = { version = "0.5", features = ["rust_1_30", "futures-01"], default_features = false }
tokio = { version = "0.1.22", features = ["codec", "fs", "io", "reactor", "rt-full", "sync", "tcp", "timer"], default_features = false }
tokio-pty-process-stream = "0.2"
tokio-signal = "0.2"
tokio-terminal-resize = "0.1"
tokio-tls = "0.2"
+tokio-tungstenite = "0.9"
ttyrec = "0.2"
twoway = "0.2"
url = "2"
diff --git a/src/web.rs b/src/web.rs
index 79ea75f..afc5128 100644
--- a/src/web.rs
+++ b/src/web.rs
@@ -1,11 +1,87 @@
+mod ws;
+
+use futures::{Future as _, Sink as _, Stream as _};
use gotham::router::builder::{DefineSingleRoute as _, DrawRoutes as _};
+use gotham::state::FromState as _;
-pub fn router() -> gotham::router::Router {
+pub fn router() -> impl gotham::handler::NewHandler {
gotham::router::builder::build_simple_router(|route| {
route.get("/").to(root);
+ route.get("/ws").to(handle_websocket_connection);
})
}
fn root(state: gotham::state::State) -> (gotham::state::State, String) {
+ log::info!("request for /");
(state, "hello world".to_string())
}
+
+fn handle_websocket_connection(
+ mut state: gotham::state::State,
+) -> (gotham::state::State, hyper::Response<hyper::Body>) {
+ let body = hyper::Body::take_from(&mut state);
+ let headers = hyper::HeaderMap::take_from(&mut state);
+ if ws::requested(&headers) {
+ let (response, stream) = match ws::accept(&headers, body) {
+ Ok(res) => res,
+ Err(_) => {
+ log::error!("failed to accept websocket request");
+ return (
+ state,
+ hyper::Response::builder()
+ .status(hyper::StatusCode::BAD_REQUEST)
+ .body(hyper::Body::empty())
+ .unwrap(),
+ );
+ }
+ };
+ let req_id = gotham::state::request_id(&state).to_owned();
+ let stream = stream
+ .map_err(|e| {
+ log::error!(
+ "error upgrading connection for websockets: {}",
+ e
+ )
+ })
+ .and_then(move |stream| handle_websocket_stream(req_id, stream));
+ tokio::spawn(stream);
+ (state, response)
+ } else {
+ (
+ state,
+ hyper::Response::new(hyper::Body::from(
+ "non-websocket request to websocket endpoint",
+ )),
+ )
+ }
+}
+
+fn handle_websocket_stream<S>(
+ req_id: String,
+ stream: S,
+) -> impl futures::Future<Item = (), Error = ()>
+where
+ S: futures::Stream<
+ Item = tokio_tungstenite::tungstenite::protocol::Message,
+ Error = tokio_tungstenite::tungstenite::Error,
+ > + futures::Sink<
+ SinkItem = tokio_tungstenite::tungstenite::protocol::Message,
+ SinkError = tokio_tungstenite::tungstenite::Error,
+ >,
+{
+ let (sink, stream) = stream.split();
+ sink.send_all(stream.map(move |msg| {
+ handle_websocket_message(&req_id, &msg);
+ msg
+ }))
+ .map_err(|e| log::error!("error during websocket stream: {}", e))
+ .map(|_| log::info!("disconnect"))
+}
+
+fn handle_websocket_message(
+ req_id: &str,
+ msg: &tokio_tungstenite::tungstenite::protocol::Message,
+) {
+ // TODO
+ log::info!("websocket stream message for {}: {:?}", req_id, msg);
+}
diff --git a/src/web/ws.rs b/src/web/ws.rs
new file mode 100644
index 0000000..ad47147
--- /dev/null
+++ b/src/web/ws.rs
@@ -0,0 +1,61 @@
+// from https://github.com/gotham-rs/gotham/blob/master/examples/websocket/src/main.rs
+
+use futures::future::Future as _;
+
+const PROTO_WEBSOCKET: &str = "websocket";
+const SEC_WEBSOCKET_KEY: &str = "Sec-WebSocket-Key";
+const SEC_WEBSOCKET_ACCEPT: &str = "Sec-WebSocket-Accept";
+
+pub fn requested(headers: &hyper::HeaderMap) -> bool {
+ headers.get(hyper::header::UPGRADE)
+ == Some(&hyper::header::HeaderValue::from_static(PROTO_WEBSOCKET))
+}
+
+pub fn accept(
+ headers: &hyper::HeaderMap,
+ body: hyper::Body,
+) -> Result<
+ (
+ hyper::Response<hyper::Body>,
+ impl futures::Future<
+ Item = tokio_tungstenite::WebSocketStream<
+ hyper::upgrade::Upgraded,
+ >,
+ Error = hyper::Error,
+ >,
+ ),
+ (),
+> {
+ let res = response(headers)?;
+ let ws = body.on_upgrade().map(|upgraded| {
+ tokio_tungstenite::WebSocketStream::from_raw_socket(
+ upgraded,
+ tokio_tungstenite::tungstenite::protocol::Role::Server,
+ None,
+ )
+ });
+
+ Ok((res, ws))
+}
+
+fn response(
+ headers: &hyper::HeaderMap,
+) -> Result<hyper::Response<hyper::Body>, ()> {
+ let key = headers.get(SEC_WEBSOCKET_KEY).ok_or(())?;
+
+ Ok(hyper::Response::builder()
+ .header(hyper::header::UPGRADE, PROTO_WEBSOCKET)
+ .header(hyper::header::CONNECTION, "upgrade")
+ .header(SEC_WEBSOCKET_ACCEPT, accept_key(key.as_bytes()))
+ .status(hyper::StatusCode::SWITCHING_PROTOCOLS)
+ .body(hyper::Body::empty())
+ .unwrap())
+}
+
+fn accept_key(key: &[u8]) -> String {
+ const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ let mut sha1 = sha1::Sha1::default();
+ sha1.update(key);
+ sha1.update(WS_GUID);
+ base64::encode(&sha1.digest().bytes())
+}