Implement nonfunctional COPY and MOVE method

Fixes #69 for now
This commit is contained in:
Lennart
2025-06-10 17:42:03 +02:00
parent 103ac0b1f9
commit 32225bdda8
7 changed files with 112 additions and 28 deletions

View File

@@ -1,4 +1,8 @@
use axum::{body::Body, extract::FromRequestParts, response::IntoResponse};
use axum::{
body::Body,
extract::{FromRequestParts, OptionalFromRequestParts},
response::IntoResponse,
};
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlError};
use thiserror::Error;
@@ -59,6 +63,21 @@ impl TryFrom<&[u8]> for Depth {
}
}
impl<S: Send + Sync> OptionalFromRequestParts<S> for Depth {
type Rejection = InvalidDepthHeader;
async fn from_request_parts(
parts: &mut axum::http::request::Parts,
_state: &S,
) -> Result<Option<Self>, Self::Rejection> {
if let Some(depth_header) = parts.headers.get("Depth") {
Ok(Some(depth_header.as_bytes().try_into()?))
} else {
Ok(None)
}
}
}
impl<S: Send + Sync> FromRequestParts<S> for Depth {
type Rejection = InvalidDepthHeader;

View File

@@ -1,9 +1,19 @@
use axum::{body::Body, extract::FromRequestParts, response::IntoResponse};
use thiserror::Error;
#[derive(Error, Debug)]
#[error("Invalid Overwrite header")]
pub struct InvalidOverwriteHeader;
impl IntoResponse for InvalidOverwriteHeader {
fn into_response(self) -> axum::response::Response {
axum::response::Response::builder()
.status(axum::http::StatusCode::BAD_REQUEST)
.body(Body::new("Invalid Overwrite header".to_string()))
.expect("this always works")
}
}
#[derive(Debug, PartialEq, Default)]
pub enum Overwrite {
#[default]
@@ -17,6 +27,21 @@ impl Overwrite {
}
}
impl<S: Send + Sync> FromRequestParts<S> for Overwrite {
type Rejection = InvalidOverwriteHeader;
async fn from_request_parts(
parts: &mut axum::http::request::Parts,
_state: &S,
) -> Result<Self, Self::Rejection> {
if let Some(overwrite_header) = parts.headers.get("Overwrite") {
overwrite_header.as_bytes().try_into()
} else {
Ok(Self::default())
}
}
}
impl TryFrom<&[u8]> for Overwrite {
type Error = InvalidOverwriteHeader;

View File

@@ -38,16 +38,6 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
None
}
#[inline]
fn copy() -> Option<MethodFunction<Self>> {
None
}
#[inline]
fn mv() -> Option<MethodFunction<Self>> {
None
}
#[inline]
fn put() -> Option<MethodFunction<Self>> {
None
@@ -58,6 +48,8 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
let mut allow = vec![
Method::from_str("PROPFIND").unwrap(),
Method::from_str("PROPPATCH").unwrap(),
Method::from_str("COPY").unwrap(),
Method::from_str("MOVE").unwrap(),
Method::DELETE,
Method::OPTIONS,
];
@@ -79,12 +71,6 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
if Self::mkcalendar().is_some() {
allow.push(Method::from_str("MKCALENDAR").unwrap());
}
if Self::copy().is_some() {
allow.push(Method::from_str("COPY").unwrap());
}
if Self::mv().is_some() {
allow.push(Method::from_str("MOVE").unwrap());
}
if Self::put().is_some() {
allow.push(Method::PUT);
}

View File

@@ -1,5 +1,9 @@
use super::methods::{axum_route_propfind, axum_route_proppatch};
use crate::resource::{ResourceService, axum_methods::AxumMethods};
use crate::resource::{
ResourceService,
axum_methods::AxumMethods,
methods::{axum_route_copy, axum_route_move},
};
use axum::{
body::Body,
extract::FromRequestParts,
@@ -51,12 +55,18 @@ where
Handler::with_state(axum_route_proppatch::<RS>, self.resource_service.clone());
let mut delete_service =
Handler::with_state(axum_route_delete::<RS>, self.resource_service.clone());
let mut move_service =
Handler::with_state(axum_route_move::<RS>, self.resource_service.clone());
let mut copy_service =
Handler::with_state(axum_route_copy::<RS>, self.resource_service.clone());
let mut options_service = Handler::with_state(route_options::<RS>, ());
match req.method().as_str() {
"PROPFIND" => return Box::pin(Service::call(&mut propfind_service, req)),
"PROPPATCH" => return Box::pin(Service::call(&mut proppatch_service, req)),
"DELETE" => return Box::pin(Service::call(&mut delete_service, req)),
"OPTIONS" => return Box::pin(Service::call(&mut options_service, req)),
"MOVE" => return Box::pin(Service::call(&mut move_service, req)),
"COPY" => return Box::pin(Service::call(&mut copy_service, req)),
"REPORT" => {
if let Some(svc) = RS::report() {
return svc(self.resource_service.clone(), req);
@@ -87,16 +97,6 @@ where
return svc(self.resource_service.clone(), req);
}
}
"COPY" => {
if let Some(svc) = RS::copy() {
return svc(self.resource_service.clone(), req);
}
}
"MOVE" => {
if let Some(svc) = RS::mv() {
return svc(self.resource_service.clone(), req);
}
}
"PUT" => {
if let Some(svc) = RS::put() {
return svc(self.resource_service.clone(), req);

View File

@@ -0,0 +1,25 @@
use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
};
use http::StatusCode;
use tracing::instrument;
use crate::{
header::{Depth, Overwrite},
resource::ResourceService,
};
#[instrument(skip(_path, _resource_service,))]
pub(crate) async fn axum_route_copy<R: ResourceService>(
Path(_path): Path<R::PathComponents>,
State(_resource_service): State<R>,
depth: Option<Depth>,
principal: R::Principal,
overwrite: Overwrite,
) -> Result<Response, R::Error> {
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
// can return a 403 error
let _depth = depth.unwrap_or(Depth::Infinity);
Ok(StatusCode::FORBIDDEN.into_response())
}

View File

@@ -1,7 +1,11 @@
mod copy;
mod delete;
mod mv;
mod propfind;
mod proppatch;
pub(crate) use copy::axum_route_copy;
pub(crate) use delete::axum_route_delete;
pub(crate) use mv::axum_route_move;
pub(crate) use propfind::axum_route_propfind;
pub(crate) use proppatch::axum_route_proppatch;

View File

@@ -0,0 +1,25 @@
use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
};
use http::StatusCode;
use tracing::instrument;
use crate::{
header::{Depth, Overwrite},
resource::ResourceService,
};
#[instrument(skip(_path, _resource_service,))]
pub(crate) async fn axum_route_move<R: ResourceService>(
Path(_path): Path<R::PathComponents>,
State(_resource_service): State<R>,
depth: Option<Depth>,
principal: R::Principal,
overwrite: Overwrite,
) -> Result<Response, R::Error> {
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
// can return a 403 error
let _depth = depth.unwrap_or(Depth::Infinity);
Ok(StatusCode::FORBIDDEN.into_response())
}