aster_forge_webdav/
axum.rs1use axum::{
4 body::{Body, to_bytes},
5 extract::Request,
6 response::{IntoResponse, Response},
7};
8use futures::StreamExt;
9use http::{HeaderMap, HeaderName, HeaderValue, StatusCode, Uri};
10
11use crate::{
12 DavBodyPolicy, DavMethod, DavRequestHead, DavRequestOrigin, DavRequestTarget, DavResponse,
13 DavResponseBody, protocol::DavProtocolError,
14};
15
16pub enum DavPreparedBody {
17 None,
18 Xml(Vec<u8>),
19 Bytes(Vec<u8>),
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
23pub enum DavBodyError {
24 #[error("failed to read WebDAV request body")]
25 ReadFailed,
26 #[error("WebDAV request body is too large")]
27 BodyTooLarge,
28 #[error("WebDAV method does not accept a request body")]
29 BodyNotAllowed,
30}
31
32pub fn request_head(
38 request: &Request,
39 mount_path: &str,
40) -> Result<Option<DavRequestHead>, DavProtocolError> {
41 let target = request_target(request, mount_path)?;
42 let Some(method) = DavMethod::from_name(request.method().as_str()) else {
43 return Ok(None);
44 };
45 let headers = convert_header_map(request.headers())?;
46 DavRequestHead::parse_known_method(method, &target, &headers).map(Some)
47}
48
49pub fn request_target<'a>(
55 request: &Request,
56 mount_path: &'a str,
57) -> Result<DavRequestTarget<'a>, DavProtocolError> {
58 let uri: Uri = request
59 .uri()
60 .to_string()
61 .parse()
62 .map_err(|_| DavProtocolError::bad_request("Invalid request URI"))?;
63 let origin = DavRequestOrigin {
64 scheme: request
65 .uri()
66 .scheme_str()
67 .or_else(|| {
68 request
69 .headers()
70 .get("x-forwarded-proto")
71 .and_then(|v| v.to_str().ok())
72 })
73 .unwrap_or("http")
74 .to_string(),
75 host: request
76 .headers()
77 .get("host")
78 .and_then(|v| v.to_str().ok())
79 .unwrap_or_default()
80 .to_string(),
81 };
82 DavRequestHead::parse_target(&uri, mount_path, &origin)
83}
84
85pub fn into_response(response: DavResponse) -> Response {
86 let mut builder = Response::builder().status(response.status);
87 for (name, value) in &response.headers {
88 builder = builder.header(name, value);
89 }
90 let body =
91 match response.body {
92 DavResponseBody::Empty => Body::empty(),
93 DavResponseBody::Bytes(body) => Body::from(body),
94 DavResponseBody::Stream(stream) => Body::from_stream(stream.map(|item| {
95 item.map_err(|_| std::io::Error::other("WebDAV response stream failed"))
96 })),
97 DavResponseBody::MultiStatus(stream) => Body::from_stream(stream.map(|item| {
98 item.map_err(|_| std::io::Error::other("WebDAV response stream failed"))
99 })),
100 };
101 builder.body(body).unwrap_or_else(|_| {
102 (StatusCode::INTERNAL_SERVER_ERROR, "invalid WebDAV response").into_response()
103 })
104}
105
106#[must_use]
107pub fn protocol_error_response(error: &DavProtocolError) -> Response {
109 into_response(crate::protocol_error_response(error))
110}
111
112pub fn convert_header_map(source: &HeaderMap) -> Result<HeaderMap, DavProtocolError> {
118 let mut headers = HeaderMap::with_capacity(source.len());
119 for (name, value) in source {
120 let name = HeaderName::from_bytes(name.as_str().as_bytes())
121 .map_err(|_| DavProtocolError::bad_request("Invalid request header"))?;
122 let value = HeaderValue::from_bytes(value.as_bytes())
123 .map_err(|_| DavProtocolError::bad_request("Invalid request header"))?;
124 headers.append(name, value);
125 }
126 Ok(headers)
127}
128
129pub async fn prepare_request_body(
135 policy: DavBodyPolicy,
136 request: Request,
137) -> Result<DavPreparedBody, DavBodyError> {
138 match policy {
139 DavBodyPolicy::Empty => {
140 let body = to_bytes(request.into_body(), 1)
141 .await
142 .map_err(|_| DavBodyError::ReadFailed)?;
143 if body.is_empty() {
144 Ok(DavPreparedBody::None)
145 } else {
146 Err(DavBodyError::BodyNotAllowed)
147 }
148 }
149 DavBodyPolicy::BoundedXml { maximum } => to_bytes(request.into_body(), maximum)
150 .await
151 .map(|b| DavPreparedBody::Xml(b.to_vec()))
152 .map_err(|error| {
153 if error.to_string().contains("length limit") {
154 DavBodyError::BodyTooLarge
155 } else {
156 DavBodyError::ReadFailed
157 }
158 }),
159 DavBodyPolicy::Bounded { maximum } => to_bytes(request.into_body(), maximum)
160 .await
161 .map(|b| DavPreparedBody::Bytes(b.to_vec()))
162 .map_err(|error| {
163 if error.to_string().contains("length limit") {
164 DavBodyError::BodyTooLarge
165 } else {
166 DavBodyError::ReadFailed
167 }
168 }),
169 DavBodyPolicy::Stream | DavBodyPolicy::Unused => Ok(DavPreparedBody::None),
170 }
171}