1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::time::Duration;
use crate::hooks::HookConfig;
/// Cross-origin resource sharing settings (tus clients in browsers).
#[derive(Debug, Clone)]
pub struct CorsConfig {
/// When false, no CORS headers are added.
pub enabled: bool,
/// `Access-Control-Allow-Origin` value (e.g. `"*"` or `"https://example.com"`).
pub allow_origin: String,
/// `Access-Control-Allow-Credentials`.
pub allow_credentials: bool,
/// Extra header names merged into `Access-Control-Allow-Headers` (tus defaults are always included).
pub extra_allow_headers: Vec<String>,
/// Extra header names merged into `Access-Control-Expose-Headers` (tus defaults are always included).
pub extra_expose_headers: Vec<String>,
/// `Access-Control-Max-Age` for preflight (seconds).
pub max_age: u64,
}
impl Default for CorsConfig {
fn default() -> Self {
Self {
enabled: false,
allow_origin: "*".to_string(),
allow_credentials: false,
extra_allow_headers: Vec::new(),
extra_expose_headers: Vec::new(),
max_age: 86400,
}
}
}
/// Runtime flags controlling which tus protocol extensions are active.
///
/// These are runtime config, not Cargo features, so one compiled binary can
/// serve any combination without recompilation.
#[derive(Debug, Clone)]
pub struct Extensions {
/// POST to create a new upload (required by most clients).
pub creation: bool,
/// POST with body to combine creation and first chunk.
pub creation_with_upload: bool,
/// Allow `Upload-Defer-Length: 1` to defer declaring size at creation.
pub creation_defer_length: bool,
/// Attach expiry timestamps to incomplete uploads.
pub expiration: bool,
/// How long incomplete uploads live before expiry. Used when `expiration = true`.
pub expiration_ttl: Option<Duration>,
/// Validate chunk integrity via `Upload-Checksum` header.
pub checksum: bool,
/// Accept checksum in HTTP trailers (requires HTTP/1.1 chunked or HTTP/2).
pub checksum_trailer: bool,
/// Allow clients to DELETE uploads.
pub termination: bool,
/// Allow parallel partial uploads assembled into a final upload.
pub concatenation: bool,
/// After a successful `final` concatenation, delete partial upload resources.
pub cleanup_concat_partials: bool,
}
impl Default for Extensions {
fn default() -> Self {
Self {
creation: true,
creation_with_upload: true,
creation_defer_length: true,
expiration: false,
expiration_ttl: None,
checksum: true,
checksum_trailer: false,
termination: true,
concatenation: false,
cleanup_concat_partials: false,
}
}
}
/// Top-level handler configuration.
#[derive(Debug, Clone)]
pub struct Config {
/// URL path prefix where upload resources are rooted, e.g. `"/files/"`.
/// Used to build the `Location` header value on POST responses.
pub base_path: String,
/// Optional absolute base URL override (e.g. `"https://uploads.example.com"`).
/// When `None` the handler builds the Location from the request's `Host` header.
pub base_url: Option<String>,
/// Maximum allowed `Upload-Length` in bytes. `0` means no server-imposed limit.
pub max_size: u64,
/// Enabled protocol extensions.
pub extensions: Extensions,
/// How long to wait when acquiring a per-upload lock before returning 408.
pub lock_timeout: Duration,
/// CORS headers on responses.
pub cors: CorsConfig,
/// When `base_url` is unset, use `X-Forwarded-Proto` / `X-Forwarded-Host` to build absolute
/// URLs (e.g. behind TLS termination). **Only enable when this service is not directly exposed
/// to untrusted clients** (forwarded headers can be spoofed).
pub trust_forwarded_headers: bool,
/// Hook callbacks and event-channel configuration.
pub hooks: HookConfig,
/// Allow HTTP GET on upload URLs to download completed data (tus-style downloads).
/// When `false`, GET returns 405.
pub enable_download: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
base_path: "/files/".to_string(),
base_url: None,
max_size: 0,
extensions: Extensions::default(),
lock_timeout: Duration::from_secs(20),
cors: CorsConfig::default(),
trust_forwarded_headers: false,
hooks: HookConfig::default(),
enable_download: false,
}
}
}