aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorLukas Stabe <lukas@stabe.de>2025-02-05 23:03:05 +0000
committerLukas Stabe <lukas@stabe.de>2025-02-05 23:03:05 +0000
commit317bd6a5d42a83c9c5e874788282a6e76f638211 (patch)
tree0907869057fea553c26601aaca6d2443fcc9de97 /src/main.rs
parentMerge pull request #1470 from svenstaro/dependabot/cargo/all-dependencies-184... (diff)
downloadminiserve-317bd6a5d42a83c9c5e874788282a6e76f638211.tar.gz
miniserve-317bd6a5d42a83c9c5e874788282a6e76f638211.zip
add read-only webdav support
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs51
1 files changed, 49 insertions, 2 deletions
diff --git a/src/main.rs b/src/main.rs
index 1434a0c..ccf611c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,13 +6,18 @@ use std::time::Duration;
use actix_files::NamedFile;
use actix_web::{
dev::{fn_service, ServiceRequest, ServiceResponse},
- http::header::ContentType,
+ guard,
+ http::{header::ContentType, Method},
middleware, web, App, HttpRequest, HttpResponse, Responder,
};
use actix_web_httpauth::middleware::HttpAuthentication;
use anyhow::Result;
use clap::{crate_version, CommandFactory, Parser};
use colored::*;
+use dav_server::{
+ actix::{DavRequest, DavResponse},
+ DavConfig, DavHandler, DavMethodSet,
+};
use fast_qr::QRBuilder;
use log::{error, warn};
@@ -27,9 +32,11 @@ mod file_utils;
mod listing;
mod pipe;
mod renderer;
+mod webdav_fs;
use crate::config::MiniserveConfig;
use crate::errors::{RuntimeError, StartupError};
+use crate::webdav_fs::RestrictedFs;
static STYLESHEET: &str = grass::include!("data/style.scss");
@@ -88,6 +95,12 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), StartupError> {
));
}
+ if miniserve_config.webdav_enabled && miniserve_config.path.is_file() {
+ return Err(StartupError::WebdavWithFileServePath(
+ miniserve_config.path.to_string_lossy().to_string(),
+ ));
+ }
+
let inside_config = miniserve_config.clone();
let canon_path = miniserve_config
@@ -307,7 +320,9 @@ fn configure_header(conf: &MiniserveConfig) -> middleware::DefaultHeaders {
/// This is where we configure the app to serve an index file, the file listing, or a single file.
fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
let dir_service = || {
- let mut files = actix_files::Files::new("", &conf.path);
+ // use routing guard so propfind and options requests fall through to the webdav handler
+ let mut files = actix_files::Files::new("", &conf.path)
+ .guard(guard::Any(guard::Get()).or(guard::Head()));
// Use specific index file if one was provided.
if let Some(ref index_file) = conf.index {
@@ -376,6 +391,38 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
// Handle directories
app.service(dir_service());
}
+
+ if conf.webdav_enabled {
+ let fs = RestrictedFs::new(&conf.path, conf.show_hidden);
+
+ let dav_server = DavHandler::builder()
+ .filesystem(fs)
+ .methods(DavMethodSet::WEBDAV_RO)
+ .hide_symlinks(conf.no_symlinks)
+ .strip_prefix(conf.route_prefix.to_owned())
+ .build_handler();
+
+ app.app_data(web::Data::new(dav_server.clone()));
+
+ app.service(
+ // actix requires tail segment to be named, even if unused
+ web::resource("/{tail}*")
+ .guard(
+ guard::Any(guard::Options())
+ .or(guard::Method(Method::from_bytes(b"PROPFIND").unwrap())),
+ )
+ .to(dav_handler),
+ );
+ }
+}
+
+async fn dav_handler(req: DavRequest, davhandler: web::Data<DavHandler>) -> DavResponse {
+ if let Some(prefix) = req.prefix() {
+ let config = DavConfig::new().strip_prefix(prefix);
+ davhandler.handle_with(config, req.request).await.into()
+ } else {
+ davhandler.handle(req.request).await.into()
+ }
}
async fn error_404(req: HttpRequest) -> Result<HttpResponse, RuntimeError> {