Update to actix-web 1.0 and use handlebars

This commit is contained in:
Fabian Freyer 2019-09-29 04:00:19 +02:00
parent d71b42665f
commit e74a974d51
7 changed files with 609 additions and 779 deletions

1260
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -16,10 +16,13 @@ travis-ci = { repository = "fubarnetes/webterm", branch = "master" }
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
actix-files = "0.1.4"
actix-service = "0.4.2"
actix-web-actors = "1.0.2"
actix-web= "1.0.8" actix-web= "1.0.8"
actix= "0.8.3" actix= "0.8.3"
askama = { version = "0.8.0", features= ["with-actix-web"] }
futures = "0.1.29" futures = "0.1.29"
handlebars = "2.0.2"
lazy_static = "1.3.0" lazy_static = "1.3.0"
libc = "0.2.62" libc = "0.2.62"
log = "0.4.8" log = "0.4.8"

View File

@ -1,5 +1,4 @@
use actix::Message; use actix::Message;
use actix_web::Binary;
use futures::{Future, Poll}; use futures::{Future, Poll};
use libc::c_ushort; use libc::c_ushort;
use tokio_pty_process::PtyMaster; use tokio_pty_process::PtyMaster;
@ -37,8 +36,8 @@ impl Message for IO {
type Result = (); type Result = ();
} }
impl Into<Binary> for IO { impl Into<actix_web::web::Bytes> for IO {
fn into(self) -> Binary { fn into(self) -> actix_web::web::Bytes {
self.0.into() self.0.into()
} }
} }
@ -49,8 +48,8 @@ impl AsRef<[u8]> for IO {
} }
} }
impl From<Binary> for IO { impl From<actix_web::web::Bytes> for IO {
fn from(b: Binary) -> Self { fn from(b: actix_web::web::Bytes) -> Self {
Self(b.as_ref().into()) Self(b.as_ref().into())
} }
} }

View File

@ -29,10 +29,11 @@
extern crate actix; extern crate actix;
extern crate actix_web; extern crate actix_web;
extern crate askama;
extern crate futures; extern crate futures;
extern crate handlebars;
extern crate libc; extern crate libc;
extern crate serde; extern crate serde;
#[macro_use]
extern crate serde_json; extern crate serde_json;
extern crate tokio; extern crate tokio;
extern crate tokio_codec; extern crate tokio_codec;
@ -41,12 +42,11 @@ extern crate tokio_pty_process;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate pretty_env_logger; extern crate pretty_env_logger;
use askama::actix_web::TemplateIntoResponse;
use actix::*; use actix::prelude::*;
use actix_web::{ws, App}; use actix::{Actor, StreamHandler};
use actix_web::{web, App, HttpRequest, HttpResponse};
use futures::prelude::*; use actix_web_actors::ws;
use std::io::Write; use std::io::Write;
use std::process::Command; use std::process::Command;
@ -55,11 +55,12 @@ use std::time::{Duration, Instant};
use tokio_codec::{BytesCodec, Decoder, FramedRead}; use tokio_codec::{BytesCodec, Decoder, FramedRead};
use tokio_pty_process::{AsyncPtyMaster, AsyncPtyMasterWriteHalf, Child, CommandExt}; use tokio_pty_process::{AsyncPtyMaster, AsyncPtyMasterWriteHalf, Child, CommandExt};
use handlebars::Handlebars;
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
mod event; mod event;
pub mod templates;
mod terminado; mod terminado;
/// Actix WebSocket actor /// Actix WebSocket actor
@ -175,6 +176,7 @@ impl StreamHandler<ws::Message, ws::ProtocolError> for Websocket {
} }
ws::Message::Binary(b) => cons.do_send(event::IO::from(b)), ws::Message::Binary(b) => cons.do_send(event::IO::from(b)),
ws::Message::Close(_) => ctx.stop(), ws::Message::Close(_) => ctx.stop(),
ws::Message::Nop => {}
}; };
} }
} }
@ -341,7 +343,7 @@ pub trait WebTermExt {
/// Serve the websocket for the webterm /// Serve the websocket for the webterm
fn webterm_socket<F>(self: Self, endpoint: &str, handler: F) -> Self fn webterm_socket<F>(self: Self, endpoint: &str, handler: F) -> Self
where where
F: Fn(&actix_web::Request) -> Command + 'static; F: Clone + Fn(&actix_web::HttpRequest) -> Command + 'static;
fn webterm_ui( fn webterm_ui(
self: Self, self: Self,
@ -351,14 +353,27 @@ pub trait WebTermExt {
) -> Self; ) -> Self;
} }
impl WebTermExt for App<()> { impl<T, B> WebTermExt for App<T, B>
where
B: actix_web::body::MessageBody,
T: actix_service::NewService<
Config = (),
Request = actix_web::dev::ServiceRequest,
Response = actix_web::dev::ServiceResponse<B>,
Error = actix_web::Error,
InitError = (),
>,
{
fn webterm_socket<F>(self: Self, endpoint: &str, handler: F) -> Self fn webterm_socket<F>(self: Self, endpoint: &str, handler: F) -> Self
where where
F: Fn(&actix_web::Request) -> Command + 'static, F: Clone + Fn(&actix_web::HttpRequest) -> Command + 'static,
{ {
self.resource(endpoint, move |r| { self.route(
r.f(move |req| ws::start(req, Websocket::new(handler(req)))) endpoint,
}) web::get().to(move |req: HttpRequest, stream: web::Payload| {
ws::start(Websocket::new(handler(&req)), &req, stream)
}),
)
} }
fn webterm_ui( fn webterm_ui(
@ -367,9 +382,23 @@ impl WebTermExt for App<()> {
webterm_socket_endpoint: &str, webterm_socket_endpoint: &str,
static_path: &str, static_path: &str,
) -> Self { ) -> Self {
let template = templates::WebTerm::new(webterm_socket_endpoint, static_path); let mut handlebars = Handlebars::new();
self.resource(endpoint, move |r| { handlebars
r.get().f(move |_| template.into_response()) .register_templates_directory(".html", "./templates")
}) .unwrap();
let handlebars_ref = web::Data::new(handlebars);
let static_path = static_path.to_owned();
let webterm_socket_endpoint = webterm_socket_endpoint.to_owned();
self.register_data(handlebars_ref.clone()).route(
endpoint,
web::get().to(move |hb: web::Data<Handlebars>| {
let data = json!({
"websocket_path": webterm_socket_endpoint,
"static_path": static_path,
});
let body = hb.render("term", &data).unwrap();
HttpResponse::Ok().body(body)
}),
)
} }
} }

View File

@ -1,11 +1,11 @@
extern crate actix; extern crate actix;
extern crate actix_web; extern crate actix_web;
extern crate webterm;
extern crate structopt; extern crate structopt;
extern crate webterm;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
use actix_web::{fs::StaticFiles, server, App}; use actix_web::{App, HttpServer};
use structopt::StructOpt; use structopt::StructOpt;
use webterm::WebTermExt; use webterm::WebTermExt;
@ -31,18 +31,12 @@ lazy_static! {
static ref OPT: Opt = Opt::from_args(); static ref OPT: Opt = Opt::from_args();
} }
fn main() { fn main() {
pretty_env_logger::init(); pretty_env_logger::init();
server::new(|| { HttpServer::new(|| {
App::new() App::new()
.handler( .service(actix_files::Files::new("/static", "./node_modules"))
"/static",
StaticFiles::new("node_modules")
.unwrap()
.show_files_listing(),
)
.webterm_socket("/websocket", |_req| { .webterm_socket("/websocket", |_req| {
let mut cmd = Command::new(OPT.command.clone()); let mut cmd = Command::new(OPT.command.clone());
cmd.env("TERM", "xterm"); cmd.env("TERM", "xterm");
@ -52,5 +46,6 @@ fn main() {
}) })
.bind(format!("{}:{}", OPT.host, OPT.port)) .bind(format!("{}:{}", OPT.host, OPT.port))
.unwrap() .unwrap()
.run(); .run()
.unwrap();
} }

View File

@ -1,20 +0,0 @@
use askama::Template;
#[derive(Template)]
#[template(path = "term.html")]
pub struct WebTerm {
websocket_path: String,
static_path: String,
}
impl WebTerm {
pub fn new<S>(websocket_path: S, static_path: S) -> Self
where
S: Into<String>,
{
Self {
websocket_path: websocket_path.into(),
static_path: static_path.into(),
}
}
}

View File

@ -7,12 +7,12 @@ SPDX-License-Identifier: BSD-3-Clause
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="stylesheet" href="{{ static_path|safe }}/xterm/dist/xterm.css" /> <link rel="stylesheet" href="{{ static_path }}/xterm/dist/xterm.css" />
<script src="{{ static_path|safe }}/xterm/dist/xterm.js"></script> <script src="{{ static_path }}/xterm/dist/xterm.js"></script>
<script src="{{ static_path|safe }}/xterm/dist/addons/attach/attach.js"></script> <script src="{{ static_path }}/xterm/dist/addons/attach/attach.js"></script>
<script src="{{ static_path|safe }}/xterm/dist/addons/terminado/terminado.js"></script> <script src="{{ static_path }}/xterm/dist/addons/terminado/terminado.js"></script>
<script src="{{ static_path|safe }}/xterm/dist/addons/fit/fit.js"></script> <script src="{{ static_path }}/xterm/dist/addons/fit/fit.js"></script>
<script src="{{ static_path|safe }}/xterm/dist/addons/search/search.js"></script> <script src="{{ static_path }}/xterm/dist/addons/search/search.js"></script>
<style> <style>
body { body {
margin: 0; margin: 0;
@ -32,7 +32,7 @@ SPDX-License-Identifier: BSD-3-Clause
var term = new Terminal(); var term = new Terminal();
var protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; var protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
var socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + "{{ websocket_path|safe }}"; var socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + "{{ websocket_path }}";
var sock = new WebSocket(socketURL); var sock = new WebSocket(socketURL);
sock.addEventListener('open', function() { sock.addEventListener('open', function() {