Skip to content

Commit e669fd4

Browse files
committed
Add param decoding, make request-handling abstract
1 parent bb61d2c commit e669fd4

4 files changed

Lines changed: 67 additions & 41 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ warp = "0.3.2"
1010
tokio = { version = "1", features = ["full"] }
1111
fnv = "1.0.7"
1212
parking_lot = "0.12.1"
13+
urlencoding = "2.1.0"
1314

1415
[features]
1516
docker = []

src/main.rs

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use parking_lot::Mutex;
55
use tokio::fs::{create_dir_all, metadata, remove_dir};
66
use tokio::join;
77
use tokio::process::{Child, Command};
8-
use warp::{http::Response, Filter};
8+
use warp::Filter;
9+
10+
mod util;
11+
use util::handle_devname;
912

1013
const DEV_LOCATION: &str = if cfg!(feature = "docker") {
1114
"/mnt/docker/"
@@ -22,7 +25,7 @@ const FUZZYFS: &str = "/usr/local/bin/fuzzyfs";
2225
const UMOUNT: &str = "/bin/umount";
2326
const UNIONFS: &str = "/usr/bin/unionfs";
2427

25-
struct HTTPResponse {
28+
pub struct HTTPResponse {
2629
status: u16,
2730
body: String,
2831
}
@@ -32,7 +35,7 @@ struct MountStatus<T: BuildHasher> {
3235
changing: HashSet<String, T>,
3336
}
3437

35-
struct LockedMountStatus<T: BuildHasher> {
38+
pub struct LockedMountStatus<T: BuildHasher> {
3639
status: Mutex<MountStatus<T>>,
3740
union: tokio::sync::Mutex<i32>,
3841
}
@@ -61,49 +64,20 @@ async fn main() {
6164
.and(warp::query::<FnvHashMap<String, String>>())
6265
// We use and_then instead of map, because this needs async capabilities.
6366
.and_then(move |map: FnvHashMap<String, String>| {
64-
let builder = Response::builder();
6567
// Increase the refcount for the global state.
6668
let shared_state = Arc::clone(&global_state);
6769
async move {
68-
// Ensure that the "devname" param is set.
69-
if let Some(name) = map.get("devname") {
70-
// If it is, mount the device.
71-
let mount_result = mount_device(name, shared_state).await;
72-
// Return the resulting status and body.
73-
builder
74-
.status(mount_result.status)
75-
.body(mount_result.body)
76-
// Any parsing Errors (there will be none) get turned into Rejections.
77-
.map_err(|_| warp::reject())
78-
} else {
79-
// Whoops, no "devname" param. Yell at the user.
80-
builder
81-
.status(400)
82-
.body("Required GET param absent: 'devname'".to_owned())
83-
.map_err(|_| warp::reject())
84-
}
70+
return handle_devname(shared_state, map, mount_device).await;
8571
}
8672
});
8773
// Pretty much the same as the previous one, not going to repeat all the comments.
8874
let umount = warp::path("umount")
8975
.and(warp::path::end())
9076
.and(warp::query::<FnvHashMap<String, String>>())
9177
.and_then(move |map: FnvHashMap<String, String>| {
92-
let builder = Response::builder();
9378
let shared_state = Arc::clone(&global_state_clone);
9479
async move {
95-
if let Some(name) = map.get("devname") {
96-
let mount_result = umount_device(name, shared_state).await;
97-
builder
98-
.status(mount_result.status)
99-
.body(mount_result.body)
100-
.map_err(|_| warp::reject())
101-
} else {
102-
builder
103-
.status(400)
104-
.body("Required GET param absent: 'devname'".to_owned())
105-
.map_err(|_| warp::reject())
106-
}
80+
return handle_devname(shared_state, map, umount_device).await;
10781
}
10882
});
10983

@@ -116,14 +90,14 @@ async fn main() {
11690

11791
/// Mounts a device, specified by the device's filename in `DEV_LOCATION`.
11892
async fn mount_device<T: BuildHasher>(
119-
device_name: &str,
93+
device_name: String,
12094
shared_state: Arc<LockedMountStatus<T>>,
12195
) -> HTTPResponse {
12296
// Construct some useful strings.
12397
// The path to the device.
124-
let devpath = DEV_LOCATION.to_owned() + device_name;
98+
let devpath = DEV_LOCATION.to_owned() + &device_name;
12599
// The fuse-archive mountpoint.
126-
let zip_mountpt = "/tmp/".to_owned() + device_name;
100+
let zip_mountpt = "/tmp/".to_owned() + &device_name;
127101
// The fuzzyfs mountpoint.
128102
let fuzzy_mountpt = zip_mountpt.clone() + ".fuzzy";
129103
// The location of the content folder inside the fuzzyfs mount.
@@ -137,15 +111,15 @@ async fn mount_device<T: BuildHasher>(
137111
if meta.is_dir() {
138112
return HTTPResponse {
139113
status: 400,
140-
body: "Requested device is a directory : ".to_owned() + device_name,
114+
body: "Requested device is a directory : ".to_owned() + &device_name,
141115
};
142116
}
143117
}
144118
// Device doesn't exist.
145119
Err(_) => {
146120
return HTTPResponse {
147121
status: 400,
148-
body: "Requested device doesn't exist: ".to_owned() + device_name,
122+
body: "Requested device doesn't exist: ".to_owned() + &device_name,
149123
};
150124
}
151125
}
@@ -302,12 +276,12 @@ async fn mount_device<T: BuildHasher>(
302276

303277
/// Unmounts a device, specified by the device's filename in `DEV_LOCATION`.
304278
async fn umount_device<T: BuildHasher>(
305-
device_name: &str,
279+
device_name: String,
306280
shared_state: Arc<LockedMountStatus<T>>,
307281
) -> HTTPResponse {
308282
// Construct some useful strings.
309283
// The fuse-archive mountpoint.
310-
let zip_mountpt = "/tmp/".to_owned() + device_name;
284+
let zip_mountpt = "/tmp/".to_owned() + &device_name;
311285
// The fuzzyfs mountpoint.
312286
let fuzzy_mountpt = zip_mountpt.clone() + ".fuzzy";
313287
// The location of the content folder inside the fuzzyfs mount.

src/util.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::{HTTPResponse, LockedMountStatus};
2+
use core::future::Future;
3+
use std::{collections::HashMap, hash::BuildHasher, sync::Arc};
4+
use urlencoding::decode;
5+
use warp::{http::Response, reject::Rejection};
6+
7+
/// Handle a request to an endpoint that needs a devname param.
8+
pub async fn handle_devname<
9+
T: BuildHasher,
10+
U: BuildHasher,
11+
F: Fn(String, Arc<LockedMountStatus<T>>) -> G,
12+
G: Future<Output = HTTPResponse>,
13+
>(
14+
shared_state: Arc<LockedMountStatus<T>>,
15+
map: HashMap<String, String, U>,
16+
handle_param: F,
17+
) -> Result<Response<String>, Rejection> {
18+
let builder = Response::builder();
19+
// Ensure that the "devname" param is set.
20+
if let Some(name) = map.get("devname") {
21+
if let Ok(decoded) = decode(name) {
22+
// If it is, mount the device.
23+
let mount_result = handle_param(decoded.into_owned(), shared_state).await;
24+
//let mount_result = mount_device(&decoded, shared_state).await;
25+
// Return the resulting status and body.
26+
builder
27+
.status(mount_result.status)
28+
.body(mount_result.body)
29+
// Any parsing Errors (there will be none) get turned into Rejections.
30+
.map_err(|_| warp::reject())
31+
} else {
32+
builder
33+
.status(400)
34+
.body("Couldn't decode devname".to_owned())
35+
.map_err(|_| warp::reject())
36+
}
37+
} else {
38+
// Whoops, no "devname" param. Yell at the user.
39+
builder
40+
.status(400)
41+
.body("Required GET param absent: 'devname'".to_owned())
42+
.map_err(|_| warp::reject())
43+
}
44+
}

0 commit comments

Comments
 (0)