webdav_handler/
warp.rs

1//! Adapter for the `warp` HTTP server framework.
2//!
3//! The filters in this module will always succeed and never
4//! return an error. For example, if a file is not found, the
5//! filter will return a 404 reply, and not an internal
6//! rejection.
7//!
8use std::convert::Infallible;
9use std::path::Path;
10
11use crate::{fakels::FakeLs, localfs::LocalFs, DavHandler};
12use warp::{filters::BoxedFilter, Filter, Reply};
13
14/// Reply-filter that runs a DavHandler.
15///
16/// Just pass in a pre-configured DavHandler. If a prefix was not
17/// configured, it will be the request path up to this point.
18pub fn dav_handler(handler: DavHandler) -> BoxedFilter<(impl Reply,)> {
19    use http::header::HeaderMap;
20    use http::uri::Uri;
21    use http::Response;
22    use warp::path::{FullPath, Tail};
23
24    warp::method()
25        .and(warp::path::full())
26        .and(warp::path::tail())
27        .and(warp::header::headers_cloned())
28        .and(warp::body::stream())
29        .and_then(
30            move |method, path_full: FullPath, path_tail: Tail, headers: HeaderMap, body| {
31                let handler = handler.clone();
32
33                async move {
34                    // rebuild an http::Request struct.
35                    let path_str = path_full.as_str();
36                    let uri = path_str.parse::<Uri>().unwrap();
37                    let mut builder = http::Request::builder().method(method).uri(uri);
38                    for (k, v) in headers.iter() {
39                        builder = builder.header(k, v);
40                    }
41                    let request = builder.body(body).unwrap();
42
43                    let response = if handler.config.prefix.is_some() {
44                        // Run a handler with the configured path prefix.
45                        handler.handle_stream(request).await
46                    } else {
47                        // Run a handler with the current path prefix.
48                        let path_len = path_str.len();
49                        let tail_len = path_tail.as_str().len();
50                        let prefix = path_str[..path_len - tail_len].to_string();
51                        let config = DavHandler::builder().strip_prefix(prefix);
52                        handler.handle_stream_with(config, request).await
53                    };
54
55                    // Need to remap the http_body::Body to a hyper::Body.
56                    let (parts, body) = response.into_parts();
57                    let response = Response::from_parts(parts, hyper::Body::wrap_stream(body));
58                    Ok::<_, Infallible>(response)
59                }
60            },
61        )
62        .boxed()
63}
64
65/// Creates a Filter that serves files and directories at the
66/// base path joined with the remainder of the request path,
67/// like `warp::filters::fs::dir`.
68///
69/// The behaviour for serving a directory depends on the flags:
70///
71/// - `index_html`: if an `index.html` file is found, serve it.
72/// - `auto_index`: create a directory listing.
73/// - no flags set: 404.
74pub fn dav_dir(base: impl AsRef<Path>, index_html: bool, auto_index: bool) -> BoxedFilter<(impl Reply,)> {
75    let mut builder = DavHandler::builder()
76        .filesystem(LocalFs::new(base, false, false, false))
77        .locksystem(FakeLs::new())
78        .autoindex(auto_index);
79    if index_html {
80        builder = builder.indexfile("index.html".to_string())
81    }
82    let handler = builder.build_handler();
83    dav_handler(handler)
84}
85
86/// Creates a Filter that serves a single file, ignoring the request path,
87/// like `warp::filters::fs::file`.
88pub fn dav_file(file: impl AsRef<Path>) -> BoxedFilter<(impl Reply,)> {
89    let handler = DavHandler::builder()
90        .filesystem(LocalFs::new_file(file, false))
91        .locksystem(FakeLs::new())
92        .build_handler();
93    dav_handler(handler)
94}