pub struct Store { /* private fields */ }fs only.Expand description
Where uploads are kept. Send + Sync; create one per process or per
route and share it by reference.
§Examples
use form_net::{SpillMode, Store};
// Files over 64 KiB spill to anonymous temporary files.
let store = Store::temp()?;
assert!(store.dir().is_some());
// Never touch the file system.
let memory = Store::memory_only().memory_threshold(1 << 20);
assert!(memory.dir().is_none());Implementations§
Source§impl Store
impl Store
Sourcepub fn temp() -> Result<Store, FormError>
pub fn temp() -> Result<Store, FormError>
A store with its own private directory under the system temporary
directory, created now: form-net-<32 hex>, owner-only on Unix,
failing if the path exists. Files up to 64 KiB stay in memory;
larger ones spill to anonymous temporary files.
§Errors
Io { kind } when the directory cannot be created; Random when the
OS random source fails (no predictable fallback name).
§Examples
let store = form_net::Store::temp()?;
let dir = store.dir().unwrap().to_owned();
assert!(dir.is_dir());
drop(store); // its own directory goes with it, once empty
assert!(!dir.exists());Sourcepub fn memory_only() -> Store
pub fn memory_only() -> Store
A store that never touches the file system: a file larger than
max_file_bytes is refused, never spilled.
§Examples
use form_net::{Limits, Policy, Store};
let store = Store::memory_only();
let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhello\r\n--X--";
let upload = form_net::read_multipart_with(b"multipart/form-data; boundary=X", &body[..], &Limits::DEFAULT, &Policy::DEFAULT, &store)?;
assert!(upload.file("f").unwrap().is_in_memory());
assert_eq!(store.dir(), None);Sourcepub fn memory_threshold(self, bytes: u64) -> Store
pub fn memory_threshold(self, bytes: u64) -> Store
Keeps each file in memory up to bytes; a file whose next write would
pass it spills first. 0 spills every file at its first byte. On a
memory_only store it changes nothing: that
store never spills, and a file larger than max_file_bytes is
refused (D-160).
§Examples
let dir = std::env::temp_dir();
let store = form_net::Store::in_dir(&dir)?.memory_threshold(16 * 1024);
assert_eq!(store.dir(), Some(dir.as_path()));Sourcepub fn max_disk_bytes(self, bytes: u64) -> Store
pub fn max_disk_bytes(self, bytes: u64) -> Store
Caps the bytes one request may put on disk (Limit { Disk } beyond).
Unset, the cap is the request’s max_body_bytes.
§Examples
let store = form_net::Store::temp()?.max_disk_bytes(50 * 1024 * 1024);
assert!(store.dir().is_some());Sourcepub fn mode(self, mode: SpillMode) -> Store
pub fn mode(self, mode: SpillMode) -> Store
How spilled files are kept.
§Examples
use form_net::{Limits, Policy, SpillMode, Store};
let store = Store::in_dir(&dir)?.memory_threshold(0).mode(SpillMode::Anonymous);
let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhello\r\n--X--";
let upload = form_net::read_multipart_with(b"multipart/form-data; boundary=X", &body[..], &Limits::DEFAULT, &Policy::DEFAULT, &store)?;
let file = upload.file("f").unwrap();
// Spilled, and nameless from the moment it was created.
assert_eq!((file.is_in_memory(), file.path()), (false, None));Sourcepub fn dir(&self) -> Option<&Path>
pub fn dir(&self) -> Option<&Path>
The directory files spill into, once it exists.
§Examples
use form_net::Store;
assert_eq!(Store::in_dir(&dir)?.dir(), Some(dir.as_path()));
assert_eq!(Store::memory_only().dir(), None);Sourcepub fn cleanup_failures(&self) -> u64
pub fn cleanup_failures(&self) -> u64
Deletions that failed during a drop (they cannot be returned).
§Examples
let store = form_net::Store::memory_only();
assert_eq!(store.cleanup_failures(), 0);
assert_eq!(store.cleanup_failures(), store.stats().cleanup_failures);Sourcepub fn stats(&self) -> StoreStats
pub fn stats(&self) -> StoreStats
What the store holds now.
§Examples
use form_net::{Limits, Policy, Store};
let store = Store::in_dir(&dir)?.memory_threshold(0);
let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhello\r\n--X--";
let upload = form_net::read_multipart_with(b"multipart/form-data; boundary=X", &body[..], &Limits::DEFAULT, &Policy::DEFAULT, &store)?;
let stats = store.stats();
assert_eq!((stats.files_on_disk, stats.bytes_on_disk, stats.files_spilled), (1, 5, 1));
drop(upload);
assert_eq!((store.stats().files_on_disk, store.stats().files_spilled), (0, 1));Sourcepub fn quota(self, budget: impl SpillBudget) -> Store
pub fn quota(self, budget: impl SpillBudget) -> Store
Adds a disk budget shared across requests (and across stores given
the same budget): bytes are reserved from it before every disk write,
and a request that would cross it is refused with
Store { QuotaExceeded } (status hint 503). Replaces an earlier one.
§Examples
use form_net::{Limits, NoInspector, Policy, SpillQuota, Store, Upload};
let quota = SpillQuota::new(4);
let store = Store::temp()?.memory_threshold(0).quota(quota.clone());
let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a\r\n\r\n12345\r\n--X--";
let e = form_net::read_multipart_with(b"multipart/form-data; boundary=X", &body[..], &Limits::DEFAULT, &Policy::DEFAULT, &store).unwrap_err();
assert_eq!(e.status_hint(), 503);
assert_eq!(quota.in_use(), 0, "the refused file gave its bytes back");Sourcepub fn memory_bound(&self, limits: &Limits) -> u64
pub fn memory_bound(&self, limits: &Limits) -> u64
The worst-case bytes one request through this store can hold in
memory under limits: Limits::memory_bound with this store’s
memory threshold (u64::MAX for a memory-only store).
§Examples
use form_net::{Limits, Store};
let limits = Limits::DEFAULT;
assert!(Store::temp()?.memory_bound(&limits) < Store::memory_only().memory_bound(&limits));Sourcepub fn sink(&self, limits: &Limits) -> StoreSink
pub fn sink(&self, limits: &Limits) -> StoreSink
The Sink that stores the files of one request under limits.
§Examples
use form_net::{Boundary, Driver, Limits, MultipartParser, NoInspector, Policy, Progress, Store};
let store = Store::memory_only();
let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
let mut driver = Driver::new(parser, store.sink(&Limits::DEFAULT));
let body = b"--X\r\nContent-Disposition: form-data; name=f; filename=a.txt\r\n\r\nhello\r\n--X--";
let (_, progress) = driver.push(body, true, &mut NoInspector)?;
assert!(matches!(progress, Progress::File(ref f) if f.bytes() == Some(&b"hello"[..])));