dav_server/
lib.rs

1//! ## Generic async HTTP/Webdav handler
2//!
3//! [`Webdav`] (RFC4918) is defined as
4//! HTTP (GET/HEAD/PUT/DELETE) plus a bunch of extension methods (PROPFIND, etc).
5//! These extension methods are used to manage collections (like unix directories),
6//! get information on collections (like unix `ls` or `readdir`), rename and
7//! copy items, lock/unlock items, etc.
8//!
9//! A `handler` is a piece of code that takes a `http::Request`, processes it in some
10//! way, and then generates a `http::Response`. This library is a `handler` that maps
11//! the HTTP/Webdav protocol to the filesystem. Or actually, "a" filesystem. Included
12//! is an adapter for the local filesystem (`localfs`), and an adapter for an
13//! in-memory filesystem (`memfs`).
14//!
15//! So this library can be used as a handler with HTTP servers like [hyper],
16//! [warp], [actix-web], etc. Either as a correct and complete HTTP handler for
17//! files (GET/HEAD) or as a handler for the entire Webdav protocol. In the latter case, you can
18//! mount it as a remote filesystem: Linux, Windows, macOS can all mount Webdav filesystems.
19//!
20//! ## Backend interfaces.
21//!
22//! The backend interfaces are similar to the ones from the Go `x/net/webdav package`:
23//!
24//! - the library contains a [HTTP handler][DavHandler].
25//! - you supply a [filesystem][DavFileSystem] for backend storage, which can optionally
26//!   implement reading/writing [DAV properties][DavProp]. If the file system requires
27//!   authorization, implement a [special trait][GuardedFileSystem].
28//! - you can supply a [locksystem][DavLockSystem] that handles webdav locks.
29//!
30//! The handler in this library works with the standard http types
31//! from the `http` and `http_body` crates. That means that you can use it
32//! straight away with http libraries / frameworks that also work with
33//! those types, like hyper. Compatibility modules for [actix-web][actix-compat]
34//! and [warp][warp-compat] are also provided.
35//!
36//! ## Implemented standards.
37//!
38//! Currently [passes the "basic", "copymove", "props", "locks" and "http"
39//! checks][README_litmus] of the Webdav Litmus Test testsuite. That's all of the base
40//! [RFC4918] webdav specification.
41//!
42//! The litmus test suite also has tests for RFC3744 "acl" and "principal",
43//! RFC5842 "bind", and RFC3253 "versioning". Those we do not support right now.
44//!
45//! The relevant parts of the HTTP RFCs are also implemented, such as the
46//! preconditions (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since,
47//! If-Range), partial transfers (Range).
48//!
49//! Also implemented is `partial PUT`, for which there are currently two
50//! non-standard ways to do it: [`PUT` with the `Content-Range` header][PUT],
51//! which is what Apache's `mod_dav` implements, and [`PATCH` with the `X-Update-Range`
52//! header][PATCH] from `SabreDav`.
53//!
54//! ## Backends.
55//!
56//! Included are two filesystems:
57//!
58//! - [`LocalFs`]: serves a directory on the local filesystem
59//! - [`MemFs`]: ephemeral in-memory filesystem. supports DAV properties.
60//!
61//! You're able to implement custom filesystem adapter:
62//!
63//! - [`DavFileSystem`]: without authorization.
64//! - [`GuardedFileSystem`]: when access control is required.
65//!
66//! Also included are two locksystems:
67//!
68//! - [`MemLs`]: ephemeral in-memory locksystem.
69//! - [`FakeLs`]: fake locksystem. just enough LOCK/UNLOCK support for macOS/Windows.
70//!
71//! External filesystem adapter implementations:
72//!
73//! - [`OpendalFs`](https://github.com/apache/opendal/tree/main/integrations/dav-server):
74//!   connects various storage protocols via [OpenDAL](https://github.com/apache/opendal).
75//!
76//! ## Example.
77//!
78//! Example server using [hyper] that serves the /tmp directory in r/w mode. You should be
79//! able to mount this network share from Linux, macOS and Windows. [Examples][examples]
80//! for other frameworks are also available.
81//!
82//! ```no_run
83//! use std::{convert::Infallible, net::SocketAddr};
84//! use hyper::{server::conn::http1, service::service_fn};
85//! use hyper_util::rt::TokioIo;
86//! use tokio::net::TcpListener;
87//! use dav_server::{fakels::FakeLs, localfs::LocalFs, DavHandler};
88//!
89//! #[tokio::main]
90//! async fn main() {
91//!     let dir = "/tmp";
92//!     let addr: SocketAddr = ([127, 0, 0, 1], 4918).into();
93//!
94//!     let dav_server = DavHandler::builder()
95//!         .filesystem(LocalFs::new(dir, false, false, false))
96//!         .locksystem(FakeLs::new())
97//!         .build_handler();
98//!
99//!     let listener = TcpListener::bind(addr).await.unwrap();
100//!
101//!     println!("Listening {addr}");
102//!
103//!     // We start a loop to continuously accept incoming connections
104//!     loop {
105//!         let (stream, _) = listener.accept().await.unwrap();
106//!         let dav_server = dav_server.clone();
107//!
108//!         // Use an adapter to access something implementing `tokio::io` traits as if they implement
109//!         // `hyper::rt` IO traits.
110//!         let io = TokioIo::new(stream);
111//!
112//!         // Spawn a tokio task to serve multiple connections concurrently
113//!         tokio::task::spawn(async move {
114//!             // Finally, we bind the incoming connection to our `hello` service
115//!             if let Err(err) = http1::Builder::new()
116//!                 // `service_fn` converts our function in a `Service`
117//!                 .serve_connection(
118//!                     io,
119//!                     service_fn({
120//!                         move |req| {
121//!                             let dav_server = dav_server.clone();
122//!                             async move { Ok::<_, Infallible>(dav_server.handle(req).await) }
123//!                         }
124//!                     }),
125//!                 )
126//!                 .await
127//!             {
128//!                 eprintln!("Failed serving: {err:?}");
129//!             }
130//!         });
131//!     }
132//! }
133//! ```
134//! [DavHandler]: struct.DavHandler.html
135//! [DavFileSystem]: fs/index.html
136//! [DavLockSystem]: ls/index.html
137//! [DavProp]: fs/struct.DavProp.html
138//! [`WebDav`]: https://tools.ietf.org/html/rfc4918
139//! [RFC4918]: https://tools.ietf.org/html/rfc4918
140//! [`MemLs`]: memls/index.html
141//! [`MemFs`]: memfs/index.html
142//! [`LocalFs`]: localfs/index.html
143//! [`FakeLs`]: fakels/index.html
144//! [actix-compat]: actix/index.html
145//! [warp-compat]: warp/index.html
146//! [README_litmus]: https://github.com/messense/dav-server-rs/blob/main/README.litmus-test.md
147//! [examples]: https://github.com/messense/dav-server-rs/tree/main/examples/
148//! [PUT]: https://github.com/messense/dav-server-rs/tree/main/doc/Apache-PUT-with-Content-Range.md
149//! [PATCH]: https://github.com/messense/dav-server-rs/tree/main/doc/SABREDAV-partialupdate.md
150//! [hyper]: https://hyper.rs/
151//! [warp]: https://crates.io/crates/warp
152//! [actix-web]: https://actix.rs/
153
154#![cfg_attr(docsrs, feature(doc_cfg))]
155
156#[macro_use]
157extern crate log;
158#[macro_use]
159extern crate lazy_static;
160
161mod async_stream;
162mod conditional;
163mod davhandler;
164mod davheaders;
165mod errors;
166mod handle_copymove;
167mod handle_delete;
168mod handle_gethead;
169mod handle_lock;
170mod handle_mkcol;
171mod handle_options;
172mod handle_props;
173mod handle_put;
174#[cfg(any(docsrs, feature = "localfs"))]
175#[cfg_attr(docsrs, doc(cfg(feature = "localfs")))]
176mod localfs_macos;
177#[cfg(any(docsrs, feature = "localfs"))]
178#[cfg_attr(docsrs, doc(cfg(feature = "localfs")))]
179mod localfs_windows;
180mod multierror;
181mod tree;
182mod util;
183mod voidfs;
184mod xmltree_ext;
185
186pub mod body;
187pub mod davpath;
188pub mod fakels;
189pub mod fs;
190#[cfg(any(docsrs, feature = "localfs"))]
191#[cfg_attr(docsrs, doc(cfg(feature = "localfs")))]
192pub mod localfs;
193pub mod ls;
194#[cfg(any(docsrs, feature = "memfs"))]
195#[cfg_attr(docsrs, doc(cfg(feature = "memfs")))]
196pub mod memfs;
197pub mod memls;
198
199#[cfg(any(docsrs, feature = "actix-compat"))]
200#[cfg_attr(docsrs, doc(cfg(feature = "actix-compat")))]
201pub mod actix;
202
203#[cfg(any(docsrs, feature = "warp-compat"))]
204#[cfg_attr(docsrs, doc(cfg(feature = "warp-compat")))]
205pub mod warp;
206
207pub(crate) use crate::davhandler::DavInner;
208pub(crate) use crate::errors::{DavError, DavResult};
209pub(crate) use crate::fs::*;
210
211pub use crate::davhandler::{DavConfig, DavHandler};
212pub use crate::util::{DavMethod, DavMethodSet};