cleanup
This commit is contained in:
parent
eb94d310e8
commit
68d4071bb9
604
Cargo.lock
generated
604
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
27
Cargo.toml
27
Cargo.toml
@ -8,29 +8,38 @@ strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread", "net", "io-util", "sync", "time"] }
|
||||
tokio = { version = "1.24.2", features = [
|
||||
"macros",
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
"io-util",
|
||||
"sync",
|
||||
"time",
|
||||
] }
|
||||
time = { version = "0.3.20", features = ["local-offset", "macros"] }
|
||||
bytemuck = { version = "1.13.0", features = ["derive"] }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.91"
|
||||
hyper = { version = "0.14.24", optional = true, features = ["server", "http1", "tcp", "stream"] }
|
||||
hyper = { version = "1.0.1", optional = true, features = ["server", "http1"] }
|
||||
futures = { version = "0.3.27", default-features = false, features = ["std"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["time"] }
|
||||
console-subscriber = { version = "0.1.8", optional = true }
|
||||
console-subscriber = { version = "0.2.0", optional = true }
|
||||
once_cell = "1.17.1"
|
||||
eyre = "0.6.8"
|
||||
color-eyre = "0.6.2"
|
||||
tracing-error = "0.2.0"
|
||||
zerocopy = "0.6.1"
|
||||
zerocopy = { version = "0.7.26", features = ["derive"] }
|
||||
tokio-stream = { version = "0.1.14", features = ["sync"] }
|
||||
flate2 = { version = "1.0.26", optional = true }
|
||||
bytes = "1.4.0"
|
||||
smallvec = { version = "1.10.0", features = ["serde", "const_generics"] }
|
||||
http-body-util = "0.1.0"
|
||||
pin-project = "1.1.3"
|
||||
async-stream = "0.3.5"
|
||||
|
||||
[build-dependencies]
|
||||
minify-js = { version = "0.5.6", optional = true }
|
||||
@ -40,5 +49,11 @@ flate2 = { version = "1.0.26", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["debug_server"]
|
||||
debug_server = ["dep:hyper", "minify-html", "dep:minify-js", "dep:css-minify", "dep:flate2"]
|
||||
debug_server = [
|
||||
"dep:hyper",
|
||||
"minify-html",
|
||||
"dep:minify-js",
|
||||
"dep:css-minify",
|
||||
"dep:flate2",
|
||||
]
|
||||
tokio_console = ["dep:console-subscriber"]
|
||||
|
246
src/http.rs
246
src/http.rs
@ -1,29 +1,114 @@
|
||||
use bytes::BytesMut;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::Future;
|
||||
use http_body_util::combinators::UnsyncBoxBody;
|
||||
use http_body_util::BodyExt;
|
||||
use http_body_util::StreamBody;
|
||||
use hyper::body::{Body, Frame};
|
||||
use hyper::header::{ACCEPT_ENCODING, CACHE_CONTROL, CONTENT_ENCODING, CONTENT_TYPE};
|
||||
use hyper::rt::Executor;
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Method, Request, Response, Server, StatusCode};
|
||||
use std::convert::Infallible;
|
||||
use hyper::service::service_fn;
|
||||
use hyper::{Method, Request, Response, StatusCode};
|
||||
use std::io::Read;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_stream::wrappers::{IntervalStream, WatchStream};
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::error;
|
||||
|
||||
use zerocopy::{AsBytes, FromBytes, LittleEndian, Unaligned};
|
||||
|
||||
use tracing::{debug, instrument};
|
||||
use zerocopy::{AsBytes, FromBytes, FromZeroes, LittleEndian, Unaligned};
|
||||
|
||||
use crate::constants::DEBUG_SERVER_PING_INTERVAL;
|
||||
use crate::packets::{Header, Packet};
|
||||
|
||||
use crate::ports::PortHandler;
|
||||
use crate::spawn;
|
||||
|
||||
mod tokio_io {
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[pin_project::pin_project]
|
||||
pub struct TokioIo<T> {
|
||||
#[pin]
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> TokioIo<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> hyper::rt::Read for TokioIo<T>
|
||||
where
|
||||
T: tokio::io::AsyncRead,
|
||||
{
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
mut buf: hyper::rt::ReadBufCursor<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
let n = unsafe {
|
||||
let mut temp_buf = tokio::io::ReadBuf::uninit(buf.as_mut());
|
||||
match tokio::io::AsyncRead::poll_read(self.project().inner, cx, &mut temp_buf) {
|
||||
Poll::Ready(Ok(())) => temp_buf.filled().len(),
|
||||
other => return other,
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
buf.advance(n);
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> hyper::rt::Write for TokioIo<T>
|
||||
where
|
||||
T: tokio::io::AsyncWrite,
|
||||
{
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
tokio::io::AsyncWrite::is_write_vectored(&self.inner)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
tokio::io::AsyncWrite::poll_write_vectored(self.project().inner, cx, bufs)
|
||||
}
|
||||
}
|
||||
}
|
||||
use self::tokio_io::TokioIo;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NamedExecutor;
|
||||
impl<T: Send + 'static, Fut: Future<Output = T> + Send + 'static> Executor<Fut> for NamedExecutor {
|
||||
@ -34,7 +119,26 @@ impl<T: Send + 'static, Fut: Future<Output = T> + Send + 'static> Executor<Fut>
|
||||
|
||||
const COMPRESSED_HTML: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/minified.html.gz"));
|
||||
|
||||
fn index(req: &Request<Body>) -> Result<Response<Body>, hyper::http::Error> {
|
||||
type ResponseBody = UnsyncBoxBody<Bytes, hyper::Error>;
|
||||
type RouteResponse = Result<Response<ResponseBody>, hyper::http::Error>;
|
||||
|
||||
fn full<D, B>(buf: B) -> ResponseBody
|
||||
where
|
||||
http_body_util::Full<D>: From<B> + BodyExt + Body<Data = Bytes>,
|
||||
D: Send + 'static,
|
||||
{
|
||||
http_body_util::Full::from(buf)
|
||||
.map_err(|_| unreachable!())
|
||||
.boxed_unsync()
|
||||
}
|
||||
|
||||
fn empty() -> ResponseBody {
|
||||
http_body_util::Empty::new()
|
||||
.map_err(|_| unreachable!())
|
||||
.boxed_unsync()
|
||||
}
|
||||
|
||||
fn index(req: &Request<hyper::body::Incoming>) -> RouteResponse {
|
||||
let response = Response::builder();
|
||||
|
||||
let accepts_gzip = req
|
||||
@ -53,9 +157,9 @@ fn index(req: &Request<Body>) -> Result<Response<Body>, hyper::http::Error> {
|
||||
response
|
||||
.header(CONTENT_ENCODING, "gzip")
|
||||
.header(CONTENT_TYPE, "text/html")
|
||||
.body(Body::from(COMPRESSED_HTML))
|
||||
.body(full(COMPRESSED_HTML))
|
||||
} else {
|
||||
let (mut sender, body) = Body::channel();
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
spawn("gunzip task", async move {
|
||||
let mut decoder =
|
||||
@ -89,43 +193,44 @@ fn index(req: &Request<Body>) -> Result<Response<Body>, hyper::http::Error> {
|
||||
|
||||
chunk.truncate(i);
|
||||
|
||||
if sender.send_data(chunk.freeze()).await.is_err() {
|
||||
if sender.send(chunk.freeze()).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
response.body(body)
|
||||
response.body(
|
||||
StreamBody::new(async_stream::stream! {
|
||||
while let Some(item) = receiver.recv().await {
|
||||
yield Ok(Frame::data(item));
|
||||
}
|
||||
})
|
||||
.boxed_unsync(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async fn data(
|
||||
_req: &Request<Body>,
|
||||
port_handler: Arc<Mutex<PortHandler>>,
|
||||
) -> Result<Response<Body>, hyper::http::Error> {
|
||||
_req: &Request<hyper::body::Incoming>,
|
||||
port_handler: &Mutex<PortHandler>,
|
||||
) -> RouteResponse {
|
||||
let res = Response::builder().header(CACHE_CONTROL, "no-store");
|
||||
|
||||
match serde_json::to_string(&*port_handler.lock().await) {
|
||||
Ok(data) => res
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(data)),
|
||||
.body(full(data)),
|
||||
Err(err) => {
|
||||
error!(%err, "failed to serialize data for debug server");
|
||||
res.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(Body::from(""))
|
||||
res.status(StatusCode::INTERNAL_SERVER_ERROR).body(empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn events(
|
||||
_req: &Request<Body>,
|
||||
_req: &Request<hyper::body::Incoming>,
|
||||
change_receiver: tokio::sync::watch::Receiver<std::time::Instant>,
|
||||
) -> Result<Response<Body>, hyper::http::Error> {
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(CACHE_CONTROL, "no-store")
|
||||
.header(CONTENT_TYPE, "text/event-stream")
|
||||
.body(Body::wrap_stream({
|
||||
WatchStream::new(change_receiver)
|
||||
) -> RouteResponse {
|
||||
let stream = WatchStream::new(change_receiver)
|
||||
.map(|x| ("change", x))
|
||||
.merge(
|
||||
IntervalStream::new(tokio::time::interval(DEBUG_SERVER_PING_INTERVAL))
|
||||
@ -137,11 +242,31 @@ fn events(
|
||||
.ok()?
|
||||
.as_secs();
|
||||
|
||||
Some(Ok::<_, Infallible>(format!(
|
||||
Some(Ok::<Frame<Bytes>, _>(Frame::data(Bytes::from(format!(
|
||||
"event: {kind}\ndata: {timestamp}\n\n"
|
||||
)))
|
||||
})
|
||||
}))
|
||||
)))))
|
||||
});
|
||||
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(CACHE_CONTROL, "no-store")
|
||||
.header(CONTENT_TYPE, "text/event-stream")
|
||||
.body(StreamBody::new(stream).boxed_unsync())
|
||||
}
|
||||
|
||||
async fn debug_server_routes(
|
||||
req: Request<hyper::body::Incoming>,
|
||||
port_handler: Arc<Mutex<PortHandler>>,
|
||||
change_receiver: tokio::sync::watch::Receiver<std::time::Instant>,
|
||||
) -> RouteResponse {
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::GET, "/") => index(&req),
|
||||
(&Method::GET, "/data") => Ok(data(&req, &port_handler).await?),
|
||||
(&Method::GET, "/events") => events(&req, change_receiver),
|
||||
_ => Ok(Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(empty())?),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn debug_server(
|
||||
@ -149,32 +274,47 @@ pub async fn debug_server(
|
||||
port_handler: Arc<Mutex<PortHandler>>,
|
||||
change_receiver: tokio::sync::watch::Receiver<std::time::Instant>,
|
||||
) {
|
||||
let server = Server::bind(&addr)
|
||||
.executor(NamedExecutor)
|
||||
.serve(make_service_fn(move |_conn| {
|
||||
let port_handler = port_handler.clone();
|
||||
let change_receiver = change_receiver.clone();
|
||||
async move {
|
||||
Ok::<_, Infallible>(service_fn(move |req| {
|
||||
let listener = TcpListener::bind(addr).await;
|
||||
|
||||
let listener = match listener {
|
||||
Ok(listener) => listener,
|
||||
Err(error) => {
|
||||
error!(%error, %addr, "failed to bind debug server");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let conn = listener.accept().await;
|
||||
|
||||
let stream = match conn {
|
||||
Ok((stream, _)) => stream,
|
||||
Err(error) => {
|
||||
error!(%error, "failed accept debug server connection");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let io = TokioIo::new(stream);
|
||||
|
||||
let port_handler = port_handler.clone();
|
||||
let change_receiver = change_receiver.clone();
|
||||
|
||||
async move {
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::GET, "/") => index(&req),
|
||||
(&Method::GET, "/data") => data(&req, port_handler).await,
|
||||
(&Method::GET, "/events") => events(&req, change_receiver),
|
||||
_ => Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::empty()),
|
||||
tokio::task::spawn(async move {
|
||||
if let Err(error) = hyper::server::conn::http1::Builder::new()
|
||||
.serve_connection(
|
||||
io,
|
||||
service_fn(move |req| {
|
||||
debug_server_routes(req, port_handler.clone(), change_receiver.clone())
|
||||
}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if !error.is_incomplete_message() {
|
||||
error!(%error, "Failed to serve debug server connection");
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}));
|
||||
|
||||
if let Err(error) = server.await {
|
||||
error!(%error, "debug server error");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +328,7 @@ struct PeerQuery {
|
||||
number: U32,
|
||||
}
|
||||
|
||||
#[derive(FromBytes, Unaligned, Debug)]
|
||||
#[derive(FromBytes, FromZeroes, Unaligned, Debug)]
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
struct PeerReply {
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
<div id="header">
|
||||
<p id="free_ports" />
|
||||
<!--<p id="last_change" />-->
|
||||
<div id="connected" class="hidden"><!--INSERT SVG HERE--></div>
|
||||
<p id="last_ping" />
|
||||
</div>
|
||||
@ -27,7 +26,6 @@
|
||||
<th onclick="sort(this,'status')">Zustand</th>
|
||||
<th onclick="sort(this,'last_change')">Seit</th>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
|
33
web/main.js
33
web/main.js
@ -53,9 +53,6 @@ window.onload = () => {
|
||||
}
|
||||
|
||||
sort = (element, key) => {
|
||||
|
||||
console.log(key, oldkey, direction);
|
||||
|
||||
if (key == oldkey) {
|
||||
direction *= -1;
|
||||
} else {
|
||||
@ -64,7 +61,6 @@ window.onload = () => {
|
||||
}
|
||||
|
||||
for (let child of table_elem.firstChild.firstChild.children) {
|
||||
console.log(child);
|
||||
child.className = ""
|
||||
};
|
||||
|
||||
@ -77,14 +73,11 @@ window.onload = () => {
|
||||
|
||||
let is_number = !!~(["port", "number", "last_change"].indexOf(key));
|
||||
|
||||
console.log("is number:", is_number);
|
||||
|
||||
table = table.sort((a, b) => (direction * (
|
||||
is_number ? a[key] - b[key] : ('' +a[key]).localeCompare(b[key], "de-DE")
|
||||
)));
|
||||
|
||||
print_table();
|
||||
console.log(table);
|
||||
populate_table();
|
||||
};
|
||||
|
||||
let fmt = Intl.DateTimeFormat('de-DE', { dateStyle: 'medium', timeStyle: 'medium' });
|
||||
@ -92,14 +85,14 @@ window.onload = () => {
|
||||
let format_date = date => fmt.format(date).replace(', ', ' ');
|
||||
let format_time = date => fmt.format(date).split(', ', 2)[1];
|
||||
|
||||
let print_table = () => {
|
||||
while(table_elem.children.length > 1) {
|
||||
table_elem.removeChild(table_elem.lastChild);
|
||||
let populate_table = () => {
|
||||
// clear everything except for the header row
|
||||
while(table_elem.rows.length > 1) {
|
||||
table_elem.deleteRow(-1);
|
||||
}
|
||||
|
||||
for (let row of table) {
|
||||
|
||||
let tr = document.createElement("tr");
|
||||
let tr = table_elem.insertRow(-1);
|
||||
|
||||
let values = [
|
||||
row.name === null ? "?" : row.name,
|
||||
@ -121,7 +114,7 @@ window.onload = () => {
|
||||
let value = values[i];
|
||||
let name = names[i];
|
||||
|
||||
let td = document.createElement("td");
|
||||
let td = tr.insertCell(-1);
|
||||
td.className = name;
|
||||
|
||||
if (name == "last_change") {
|
||||
@ -139,17 +132,11 @@ window.onload = () => {
|
||||
} else {
|
||||
td.innerText = value;
|
||||
}
|
||||
tr.appendChild(td);
|
||||
|
||||
}
|
||||
|
||||
table_elem.appendChild(tr)
|
||||
}
|
||||
};
|
||||
|
||||
let update_table = data => {
|
||||
console.log(data);
|
||||
|
||||
const allowed_ports = data.allowed_ports.map(x => x.end - x.start + 1).reduce((a,b) => a + b, 0);
|
||||
free_ports.innerText = `Freie Ports: ${allowed_ports - Object.keys(data.allocated_ports).length - data.errored_ports.length}`;
|
||||
|
||||
@ -191,11 +178,9 @@ window.onload = () => {
|
||||
table.push({port, number: null, status: "Fehler", last_change: new Date(timestamp * 1000), rejector: null, name: ""})
|
||||
}
|
||||
|
||||
console.log(table);
|
||||
|
||||
do_sort(oldkey, direction);
|
||||
|
||||
print_table();
|
||||
populate_table();
|
||||
};
|
||||
|
||||
|
||||
@ -231,7 +216,7 @@ window.onload = () => {
|
||||
last_ping.innerText = `Stand: ${format_event(event, format_time)}`;
|
||||
connected.className = "visible";
|
||||
|
||||
print_table();
|
||||
populate_table();
|
||||
|
||||
clearTimeout(display_disconnected);
|
||||
display_disconnected = setTimeout(() => connected.className = "hidden", 5000);
|
||||
|
Loading…
Reference in New Issue
Block a user