support path routing (#2188)

This commit is contained in:
Aiden McClelland
2023-03-08 14:50:27 -07:00
committed by GitHub
parent ee1e92e1cb
commit eeacdc1359

View File

@@ -28,6 +28,7 @@ use crate::net::HttpHandler;
use crate::{diagnostic_api, install_api, main_api, setup_api, Error, ErrorKind, ResultExt}; use crate::{diagnostic_api, install_api, main_api, setup_api, Error, ErrorKind, ResultExt};
static NOT_FOUND: &[u8] = b"Not Found"; static NOT_FOUND: &[u8] = b"Not Found";
static METHOD_NOT_ALLOWED: &[u8] = b"Method Not Allowed";
static NOT_AUTHORIZED: &[u8] = b"Not Authorized"; static NOT_AUTHORIZED: &[u8] = b"Not Authorized";
pub const MAIN_UI_WWW_DIR: &str = "/var/www/html/main"; pub const MAIN_UI_WWW_DIR: &str = "/var/www/html/main";
@@ -238,41 +239,26 @@ async fn alt_ui(req: Request<Body>, ui_mode: UiMode) -> Result<Response<Body>, E
.filter_map(|s| s.split(";").next()) .filter_map(|s| s.split(";").next())
.map(|s| s.trim()) .map(|s| s.trim())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
match request_parts.uri.path() { match &request_parts.method {
"/" => { &Method::GET => {
let full_path = PathBuf::from(selected_root_dir).join("index.html"); let uri_path = request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path());
file_send(full_path, &accept_encoding).await let full_path = Path::new(selected_root_dir).join(uri_path);
} file_send(
_ => { if tokio::fs::metadata(&full_path).await.is_ok() {
match ( full_path
request_parts.method, } else {
request_parts Path::new(selected_root_dir).join("index.html")
.uri },
.path() &accept_encoding,
.strip_prefix('/') )
.unwrap_or(request_parts.uri.path()) .await
.split_once('/'),
) {
(Method::GET, None) => {
let uri_path = request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path());
let full_path = PathBuf::from(selected_root_dir).join(uri_path);
file_send(full_path, &accept_encoding).await
}
(Method::GET, Some((dir, file))) => {
let full_path = PathBuf::from(selected_root_dir).join(dir).join(file);
file_send(full_path, &accept_encoding).await
}
_ => Ok(not_found()),
}
} }
_ => Ok(method_not_allowed()),
} }
} }
@@ -289,114 +275,72 @@ async fn main_embassy_ui(req: Request<Body>, ctx: RpcContext) -> Result<Response
.filter_map(|s| s.split(";").next()) .filter_map(|s| s.split(";").next())
.map(|s| s.trim()) .map(|s| s.trim())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
match request_parts.uri.path() { match (
"/" => { &request_parts.method,
let full_path = PathBuf::from(selected_root_dir).join("index.html"); request_parts
.uri
file_send(full_path, &accept_encoding).await .path()
} .strip_prefix('/')
_ => { .unwrap_or(request_parts.uri.path())
let valid_session = HasValidSession::from_request_parts(&request_parts, &ctx).await; .split_once('/'),
) {
match valid_session { (&Method::GET, Some(("public", path))) => {
Ok(_valid) => { match HasValidSession::from_request_parts(&request_parts, &ctx).await {
match ( Ok(_) => {
request_parts.method, let sub_path = Path::new(path);
request_parts if let Ok(rest) = sub_path.strip_prefix("package-data") {
.uri file_send(
.path() ctx.datadir.join(PKG_PUBLIC_DIR).join(rest),
.strip_prefix('/') &accept_encoding,
.unwrap_or(request_parts.uri.path()) )
.split_once('/'), .await
) { } else if let Ok(rest) = sub_path.strip_prefix("eos") {
(Method::GET, Some(("public", path))) => { match rest.to_str() {
let sub_path = Path::new(path); Some("local.crt") => {
if let Ok(rest) = sub_path.strip_prefix("package-data") { file_send(crate::net::ssl::ROOT_CA_STATIC_PATH, &accept_encoding)
file_send( .await
ctx.datadir.join(PKG_PUBLIC_DIR).join(rest),
&accept_encoding,
)
.await
} else if let Ok(rest) = sub_path.strip_prefix("eos") {
match rest.to_str() {
Some("local.crt") => {
file_send(
crate::net::ssl::ROOT_CA_STATIC_PATH,
&accept_encoding,
)
.await
}
None => Ok(bad_request()),
_ => Ok(not_found()),
}
} else {
Ok(not_found())
} }
None => Ok(bad_request()),
_ => Ok(not_found()),
} }
(Method::GET, Some(("eos", "local.crt"))) => { } else {
file_send( Ok(not_found())
PathBuf::from(crate::net::ssl::ROOT_CA_STATIC_PATH),
&accept_encoding,
)
.await
}
(Method::GET, None) => {
let uri_path = request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path());
let full_path = PathBuf::from(selected_root_dir).join(uri_path);
file_send(full_path, &accept_encoding).await
}
(Method::GET, Some((dir, file))) => {
let full_path = PathBuf::from(selected_root_dir).join(dir).join(file);
file_send(full_path, &accept_encoding).await
}
_ => Ok(not_found()),
}
}
Err(err) => {
match (
request_parts.method,
request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path())
.split_once('/'),
) {
(Method::GET, Some(("public", _path))) => {
un_authorized(err, request_parts.uri.path())
}
(Method::GET, Some(("eos", "local.crt"))) => {
un_authorized(err, request_parts.uri.path())
}
(Method::GET, None) => {
let uri_path = request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path());
let full_path = PathBuf::from(selected_root_dir).join(uri_path);
file_send(full_path, &accept_encoding).await
}
(Method::GET, Some((dir, file))) => {
let full_path = PathBuf::from(selected_root_dir).join(dir).join(file);
file_send(full_path, &accept_encoding).await
}
_ => Ok(not_found()),
} }
} }
Err(e) => un_authorized(e, &format!("public/{path}")),
} }
} }
(&Method::GET, Some(("eos", "local.crt"))) => {
match HasValidSession::from_request_parts(&request_parts, &ctx).await {
Ok(_) => {
file_send(
PathBuf::from(crate::net::ssl::ROOT_CA_STATIC_PATH),
&accept_encoding,
)
.await
}
Err(e) => un_authorized(e, "eos/local.crt"),
}
}
(&Method::GET, _) => {
let uri_path = request_parts
.uri
.path()
.strip_prefix('/')
.unwrap_or(request_parts.uri.path());
let full_path = Path::new(selected_root_dir).join(uri_path);
file_send(
if tokio::fs::metadata(&full_path).await.is_ok() {
full_path
} else {
Path::new(selected_root_dir).join("index.html")
},
&accept_encoding,
)
.await
}
_ => Ok(method_not_allowed()),
} }
} }
@@ -417,6 +361,14 @@ fn not_found() -> Response<Body> {
.unwrap() .unwrap()
} }
/// HTTP status code 405
fn method_not_allowed() -> Response<Body> {
Response::builder()
.status(StatusCode::METHOD_NOT_ALLOWED)
.body(METHOD_NOT_ALLOWED.into())
.unwrap()
}
fn server_error(err: Error) -> Response<Body> { fn server_error(err: Error) -> Response<Body> {
Response::builder() Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
@@ -439,30 +391,31 @@ async fn file_send(
let path = path.as_ref(); let path = path.as_ref();
if let Ok(file) = File::open(path).await { let file = File::open(path)
let metadata = file.metadata().await.with_kind(ErrorKind::Filesystem)?; .await
.with_ctx(|_| (ErrorKind::Filesystem, path.display().to_string()))?;
let metadata = file
.metadata()
.await
.with_ctx(|_| (ErrorKind::Filesystem, path.display().to_string()))?;
match IsNonEmptyFile::new(&metadata, path) { match IsNonEmptyFile::new(&metadata, path) {
Some(a) => a, Some(a) => a,
None => return Ok(not_found()), None => return Ok(not_found()),
}; };
let mut builder = Response::builder().status(StatusCode::OK); let mut builder = Response::builder().status(StatusCode::OK);
builder = with_e_tag(path, &metadata, builder)?; builder = with_e_tag(path, &metadata, builder)?;
builder = with_content_type(path, builder); builder = with_content_type(path, builder);
builder = with_content_length(&metadata, builder); builder = with_content_length(&metadata, builder);
let body = if accept_encoding.contains(&"br") { let body = if accept_encoding.contains(&"br") {
Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(BufReader::new(file)))) Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(BufReader::new(file))))
} else if accept_encoding.contains(&"gzip") { } else if accept_encoding.contains(&"gzip") {
Body::wrap_stream(ReaderStream::new(GzipEncoder::new(BufReader::new(file)))) Body::wrap_stream(ReaderStream::new(GzipEncoder::new(BufReader::new(file))))
} else { } else {
Body::wrap_stream(ReaderStream::new(file)) Body::wrap_stream(ReaderStream::new(file))
}; };
return builder.body(body).with_kind(ErrorKind::Network); builder.body(body).with_kind(ErrorKind::Network)
}
tracing::debug!("File not found: {:?}", path);
Ok(not_found())
} }
struct IsNonEmptyFile(()); struct IsNonEmptyFile(());