dav_server/
lib.rs

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