From 9ea6c4f8df746468f2641ee949e3240334c92466 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Wed, 31 Jan 2024 11:23:36 -0500 Subject: [PATCH 1/2] Handle requests for `/.static/styles.css` via top-level router --- src/dav/mod.rs | 8 +------- src/main.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/dav/mod.rs b/src/dav/mod.rs index 6ac25d9..99ff8bf 100644 --- a/src/dav/mod.rs +++ b/src/dav/mod.rs @@ -9,7 +9,7 @@ use self::path::*; use self::types::*; use self::util::*; use self::xml::*; -use crate::consts::{CSS_CONTENT_TYPE, DAV_XML_CONTENT_TYPE, HTML_CONTENT_TYPE}; +use crate::consts::{DAV_XML_CONTENT_TYPE, HTML_CONTENT_TYPE}; use crate::dandi::*; use axum::{ body::Body, @@ -27,8 +27,6 @@ const WEBDAV_RESPONSE_HEADERS: [(&str, &str); 2] = [ ("DAV", "1, 3"), ]; -static STYLESHEET: &str = include_str!("static/styles.css"); - pub(crate) struct DandiDav { client: Client, templater: Templater, @@ -53,10 +51,6 @@ impl DandiDav { // compile on pre-1.74 Rusts: let m = req.method(); let resp = match m { - &Method::GET if uri_path == "/.static/styles.css" => { - // Don't add WebDAV headers - return Ok(([(CONTENT_TYPE, CSS_CONTENT_TYPE)], STYLESHEET).into_response()); - } &Method::GET => { let Some(path) = DavPath::parse_uri_path(uri_path) else { return Ok(not_found()); diff --git a/src/main.rs b/src/main.rs index 68bfa31..30971ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,17 @@ mod dandi; mod dav; mod paths; mod s3; -use crate::consts::DEFAULT_API_URL; +use crate::consts::{CSS_CONTENT_TYPE, DEFAULT_API_URL}; use crate::dandi::Client; use crate::dav::{DandiDav, Templater}; use anyhow::Context; use axum::{ body::Body, extract::Request, - http::{response::Response, Method}, + http::{header::CONTENT_TYPE, response::Response, Method}, middleware::{self, Next}, response::IntoResponse, + routing::get, Router, }; use clap::Parser; @@ -23,6 +24,8 @@ use tower::service_fn; use tower_http::trace::TraceLayer; use tracing_subscriber::filter::LevelFilter; +static STYLESHEET: &str = include_str!("dav/static/styles.css"); + /// WebDAV view to DANDI Archive /// /// See for more information. @@ -56,6 +59,13 @@ async fn main() -> anyhow::Result<()> { let templater = Templater::load()?; let dav = Arc::new(DandiDav::new(client, templater, args.title)); let app = Router::new() + .route( + "/.static/styles.css", + get(|| async { + // Note: This response should not have WebDAV headers (DAV, Allow) + ([(CONTENT_TYPE, CSS_CONTENT_TYPE)], STYLESHEET) + }), + ) .nest_service( "/", service_fn(move |req: Request| { From 663e16cd113f6538d3fa87626245bed06ac2410b Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Wed, 31 Jan 2024 11:32:37 -0500 Subject: [PATCH 2/2] Ensure all WebDAV responses have "Allow" and "DAV" headers --- src/dav/mod.rs | 25 +++++++++++++++---------- src/main.rs | 5 ++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/dav/mod.rs b/src/dav/mod.rs index 99ff8bf..02712ac 100644 --- a/src/dav/mod.rs +++ b/src/dav/mod.rs @@ -19,6 +19,7 @@ use axum::{ RequestExt, }; use futures_util::TryStreamExt; +use std::convert::Infallible; use thiserror::Error; const WEBDAV_RESPONSE_HEADERS: [(&str, &str); 2] = [ @@ -45,31 +46,35 @@ impl DandiDav { pub(crate) async fn handle_request( &self, req: Request, - ) -> Result, DavError> { + ) -> Result, Infallible> { + let resp = self.inner_handle_request(req).await; + Ok((WEBDAV_RESPONSE_HEADERS, resp).into_response()) + } + + async fn inner_handle_request(&self, req: Request) -> Result, DavError> { let uri_path = req.uri().path(); // Performing this assignment outside the `match` magically makes this // compile on pre-1.74 Rusts: let m = req.method(); - let resp = match m { + match m { &Method::GET => { let Some(path) = DavPath::parse_uri_path(uri_path) else { return Ok(not_found()); }; - self.get(&path, uri_path).await? + self.get(&path, uri_path).await } - &Method::OPTIONS => StatusCode::NO_CONTENT.into_response(), + &Method::OPTIONS => Ok(StatusCode::NO_CONTENT.into_response()), m if m.as_str().eq_ignore_ascii_case("PROPFIND") => { let Some(path) = DavPath::parse_uri_path(uri_path) else { return Ok(not_found()); }; match req.extract::<(FiniteDepth, PropFind), _>().await { - Ok((depth, pf)) => self.propfind(&path, depth, pf).await?, - Err(r) => r, + Ok((depth, pf)) => self.propfind(&path, depth, pf).await, + Err(r) => Ok(r), } } - _ => StatusCode::METHOD_NOT_ALLOWED.into_response(), - }; - Ok((WEBDAV_RESPONSE_HEADERS, resp).into_response()) + _ => Ok(StatusCode::METHOD_NOT_ALLOWED.into_response()), + } } async fn get(&self, path: &DavPath, uri_path: &str) -> Result, DavError> { @@ -357,5 +362,5 @@ impl IntoResponse for DavError { } fn not_found() -> Response { - (StatusCode::NOT_FOUND, WEBDAV_RESPONSE_HEADERS, "404\n").into_response() + (StatusCode::NOT_FOUND, "404\n").into_response() } diff --git a/src/main.rs b/src/main.rs index 30971ba..3beb24b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,12 +12,10 @@ use axum::{ extract::Request, http::{header::CONTENT_TYPE, response::Response, Method}, middleware::{self, Next}, - response::IntoResponse, routing::get, Router, }; use clap::Parser; -use std::convert::Infallible; use std::net::IpAddr; use std::sync::Arc; use tower::service_fn; @@ -70,7 +68,8 @@ async fn main() -> anyhow::Result<()> { "/", service_fn(move |req: Request| { let dav = Arc::clone(&dav); - async move { Ok::<_, Infallible>(dav.handle_request(req).await.into_response()) } + // Box the large future: + async move { Box::pin(dav.handle_request(req)).await } }), ) .layer(middleware::from_fn(handle_head))