Skip to content

Commit be5e9a8

Browse files
committed
refactoring to remove duplicate code in errors
Signed-off-by: George Pisaltu <gpl@amazon.com>
1 parent 4018b5e commit be5e9a8

File tree

6 files changed

+212
-205
lines changed

6 files changed

+212
-205
lines changed

api_server/src/parsed_request.rs

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,25 @@ impl ParsedRequest {
4545
(Method::Get, "", None) => parse_get_instance_info(),
4646
(Method::Get, "machine-config", None) => parse_get_machine_config(),
4747
(Method::Get, "mmds", None) => parse_get_mmds(),
48+
(Method::Get, _, Some(_)) => generic_error(Method::Get),
4849
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
4950
(Method::Put, "boot-source", Some(body)) => parse_put_boot_source(body),
50-
(Method::Put, "drives", maybe_body) => parse_put_drive(maybe_body, path_tokens.get(1)),
51+
(Method::Put, "drives", Some(body)) => parse_put_drive(body, path_tokens.get(1)),
5152
(Method::Put, "logger", Some(body)) => parse_put_logger(body),
52-
(Method::Put, "machine-config", maybe_body) => parse_put_machine_config(maybe_body),
53+
(Method::Put, "machine-config", Some(body)) => parse_put_machine_config(body),
5354
(Method::Put, "mmds", Some(body)) => parse_put_mmds(body),
54-
(Method::Put, "network-interfaces", maybe_body) => {
55-
parse_put_net(maybe_body, path_tokens.get(1))
55+
(Method::Put, "network-interfaces", Some(body)) => {
56+
parse_put_net(body, path_tokens.get(1))
5657
}
5758
(Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
58-
(Method::Patch, "drives", maybe_body) => {
59-
parse_patch_drive(maybe_body, path_tokens.get(1))
60-
}
61-
(Method::Patch, "machine-config", maybe_body) => parse_patch_machine_config(maybe_body),
59+
(Method::Put, _, None) => generic_error(Method::Put),
60+
(Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.get(1)),
61+
(Method::Patch, "machine-config", Some(body)) => parse_patch_machine_config(body),
6262
(Method::Patch, "mmds", Some(body)) => parse_patch_mmds(body),
63-
(Method::Patch, "network-interfaces", maybe_body) => {
64-
parse_patch_net(maybe_body, path_tokens.get(1))
63+
(Method::Patch, "network-interfaces", Some(body)) => {
64+
parse_patch_net(body, path_tokens.get(1))
6565
}
66+
(Method::Patch, _, None) => generic_error(Method::Patch),
6667
(method, unknown_uri, _) => {
6768
Err(Error::InvalidPathMethod(unknown_uri.to_string(), method))
6869
}
@@ -128,6 +129,24 @@ fn describe(method: Method, path: &str, body: Option<&Body>) -> String {
128129
}
129130
}
130131

132+
/// Generates a `GenericError` for each request method.
133+
pub fn generic_error(method: Method) -> Result<ParsedRequest, Error> {
134+
match method {
135+
Method::Get => Err(Error::Generic(
136+
StatusCode::BadRequest,
137+
"GET request cannot have a body.".to_string(),
138+
)),
139+
Method::Put => Err(Error::Generic(
140+
StatusCode::BadRequest,
141+
"Empty PUT request.".to_string(),
142+
)),
143+
Method::Patch => Err(Error::Generic(
144+
StatusCode::BadRequest,
145+
"Empty PATCH request.".to_string(),
146+
)),
147+
}
148+
}
149+
131150
#[derive(Debug)]
132151
pub enum Error {
133152
// A generic error, with a given status code and message to be turned into a fault message.
@@ -235,6 +254,73 @@ mod tests {
235254
}
236255
}
237256

257+
#[test]
258+
fn test_invalid_get() {
259+
let (mut sender, receiver) = UnixStream::pair().unwrap();
260+
let mut connection = HttpConnection::new(receiver);
261+
sender
262+
.write_all(
263+
b"GET /mmds HTTP/1.1\r\n\
264+
Content-Type: text/plain\r\n\
265+
Content-Length: 4\r\n\r\nbody",
266+
)
267+
.unwrap();
268+
assert!(connection.try_read().is_ok());
269+
let req = connection.pop_parsed_request().unwrap();
270+
match ParsedRequest::try_from_request(&req) {
271+
Err(Error::Generic(StatusCode::BadRequest, err_msg)) => {
272+
if err_msg != "GET request cannot have a body." {
273+
panic!("GET request with body.");
274+
}
275+
}
276+
_ => panic!("GET request with body."),
277+
};
278+
}
279+
280+
#[test]
281+
fn test_invalid_put() {
282+
let (mut sender, receiver) = UnixStream::pair().unwrap();
283+
let mut connection = HttpConnection::new(receiver);
284+
sender
285+
.write_all(
286+
b"PUT /mmds HTTP/1.1\r\n\
287+
Content-Type: application/json\r\n\r\n",
288+
)
289+
.unwrap();
290+
assert!(connection.try_read().is_ok());
291+
let req = connection.pop_parsed_request().unwrap();
292+
match ParsedRequest::try_from_request(&req) {
293+
Err(Error::Generic(StatusCode::BadRequest, err_msg)) => {
294+
if err_msg != "Empty PUT request." {
295+
panic!("Empty PUT request.");
296+
}
297+
}
298+
_ => panic!("Empty PUT request."),
299+
};
300+
}
301+
302+
#[test]
303+
fn test_invalid_patch() {
304+
let (mut sender, receiver) = UnixStream::pair().unwrap();
305+
let mut connection = HttpConnection::new(receiver);
306+
sender
307+
.write_all(
308+
b"PATCH /mmds HTTP/1.1\r\n\
309+
Content-Type: application/json\r\n\r\n",
310+
)
311+
.unwrap();
312+
assert!(connection.try_read().is_ok());
313+
let req = connection.pop_parsed_request().unwrap();
314+
match ParsedRequest::try_from_request(&req) {
315+
Err(Error::Generic(StatusCode::BadRequest, err_msg)) => {
316+
if err_msg != "Empty PATCH request." {
317+
panic!("Empty PATCH request.");
318+
}
319+
}
320+
_ => panic!("Empty PATCH request."),
321+
};
322+
}
323+
238324
#[test]
239325
fn test_error_into_response() {
240326
// Generic error.

api_server/src/request/drive.rs

Lines changed: 48 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use serde_json::{Map, Value};
55

66
use super::super::VmmAction;
77
use logger::{Metric, METRICS};
8-
use request::{Body, checked_id, Error, ParsedRequest, StatusCode};
8+
use request::{checked_id, Body, Error, ParsedRequest, StatusCode};
99
use vmm::vmm_config::drive::BlockDeviceConfig;
1010

1111
#[derive(Clone)]
@@ -76,10 +76,7 @@ impl PatchDrivePayload {
7676
}
7777
}
7878

79-
pub fn parse_put_drive(
80-
maybe_body: Option<&Body>,
81-
id_from_path: Option<&&str>,
82-
) -> Result<ParsedRequest, Error> {
79+
pub fn parse_put_drive(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
8380
METRICS.put_api_requests.drive_count.inc();
8481
let id = match id_from_path {
8582
Some(&id) => checked_id(id)?,
@@ -88,35 +85,25 @@ pub fn parse_put_drive(
8885
}
8986
};
9087

91-
if let Some(body) = maybe_body {
92-
let device_cfg = serde_json::from_slice::<BlockDeviceConfig>(body.raw()).map_err(|e| {
93-
METRICS.put_api_requests.drive_fails.inc();
94-
Error::SerdeJson(e)
95-
})?;
88+
let device_cfg = serde_json::from_slice::<BlockDeviceConfig>(body.raw()).map_err(|e| {
89+
METRICS.put_api_requests.drive_fails.inc();
90+
Error::SerdeJson(e)
91+
})?;
9692

97-
if id != device_cfg.drive_id {
98-
METRICS.put_api_requests.drive_fails.inc();
99-
Err(Error::Generic(
100-
StatusCode::BadRequest,
101-
"The id from the path does not match the id from the body!".to_string(),
102-
))
103-
} else {
104-
Ok(ParsedRequest::Sync(VmmAction::InsertBlockDevice(
105-
device_cfg,
106-
)))
107-
}
108-
} else {
93+
if id != device_cfg.drive_id {
94+
METRICS.put_api_requests.drive_fails.inc();
10995
Err(Error::Generic(
11096
StatusCode::BadRequest,
111-
"Empty PUT request.".to_string(),
97+
"The id from the path does not match the id from the body!".to_string(),
11298
))
99+
} else {
100+
Ok(ParsedRequest::Sync(VmmAction::InsertBlockDevice(
101+
device_cfg,
102+
)))
113103
}
114104
}
115105

116-
pub fn parse_patch_drive(
117-
maybe_body: Option<&Body>,
118-
id_from_path: Option<&&str>,
119-
) -> Result<ParsedRequest, Error> {
106+
pub fn parse_patch_drive(body: &Body, id_from_path: Option<&&str>) -> Result<ParsedRequest, Error> {
120107
METRICS.patch_api_requests.drive_count.inc();
121108
let id = match id_from_path {
122109
Some(&id) => checked_id(id)?,
@@ -125,37 +112,30 @@ pub fn parse_patch_drive(
125112
}
126113
};
127114

128-
if let Some(body) = maybe_body {
129-
METRICS.patch_api_requests.drive_count.inc();
130-
let patch_drive_payload = PatchDrivePayload {
131-
fields: serde_json::from_slice(body.raw()).map_err(|e| {
132-
METRICS.patch_api_requests.drive_fails.inc();
133-
Error::SerdeJson(e)
134-
})?,
135-
};
136-
137-
patch_drive_payload.validate()?;
138-
let drive_id: String = patch_drive_payload.get_string_field_unchecked("drive_id");
139-
let path_on_host: String = patch_drive_payload.get_string_field_unchecked("path_on_host");
140-
141-
if id != drive_id.as_str() {
115+
METRICS.patch_api_requests.drive_count.inc();
116+
let patch_drive_payload = PatchDrivePayload {
117+
fields: serde_json::from_slice(body.raw()).map_err(|e| {
142118
METRICS.patch_api_requests.drive_fails.inc();
143-
return Err(Error::Generic(
144-
StatusCode::BadRequest,
145-
String::from("The id from the path does not match the id from the body!"),
146-
));
147-
}
119+
Error::SerdeJson(e)
120+
})?,
121+
};
148122

149-
Ok(ParsedRequest::Sync(VmmAction::UpdateBlockDevicePath(
150-
drive_id,
151-
path_on_host,
152-
)))
153-
} else {
154-
Err(Error::Generic(
123+
patch_drive_payload.validate()?;
124+
let drive_id: String = patch_drive_payload.get_string_field_unchecked("drive_id");
125+
let path_on_host: String = patch_drive_payload.get_string_field_unchecked("path_on_host");
126+
127+
if id != drive_id.as_str() {
128+
METRICS.patch_api_requests.drive_fails.inc();
129+
return Err(Error::Generic(
155130
StatusCode::BadRequest,
156-
"Empty PATCH request.".to_string(),
157-
))
131+
String::from("The id from the path does not match the id from the body!"),
132+
));
158133
}
134+
135+
Ok(ParsedRequest::Sync(VmmAction::UpdateBlockDevicePath(
136+
drive_id,
137+
path_on_host,
138+
)))
159139
}
160140

161141
#[cfg(test)]
@@ -164,45 +144,43 @@ mod tests {
164144

165145
#[test]
166146
fn test_parse_patch_request() {
167-
assert!(parse_patch_drive(None, None).is_err());
168-
assert!(parse_patch_drive(None, Some(&"id")).is_err());
169-
assert!(parse_patch_drive(Some(&Body::new("invalid_payload")), Some(&"id")).is_err());
147+
assert!(parse_patch_drive(&Body::new("invalid_payload"), Some(&"id")).is_err());
170148

171149
// PATCH with invalid fields.
172150
let body = r#"{
173151
"drive_id": "bar",
174152
"is_read_only": false
175153
}"#;
176-
assert!(parse_patch_drive(Some(&Body::new(body)), Some(&"2")).is_err());
154+
assert!(parse_patch_drive(&Body::new(body), Some(&"2")).is_err());
177155

178156
// PATCH with invalid types on fields. Adding a drive_id as number instead of string.
179157
let body = r#"{
180158
"drive_id": 1000,
181159
"path_on_host": "dummy"
182160
}"#;
183-
let res = parse_patch_drive(Some(&Body::new(body)), Some(&"1000"));
161+
let res = parse_patch_drive(&Body::new(body), Some(&"1000"));
184162
assert!(res.is_err());
185163

186164
// PATCH with invalid types on fields. Adding a path_on_host as bool instead of string.
187165
let body = r#"{
188166
"drive_id": 1000,
189167
"path_on_host": true
190168
}"#;
191-
let res = parse_patch_drive(Some(&Body::new(body)), Some(&"1000"));
169+
let res = parse_patch_drive(&Body::new(body), Some(&"1000"));
192170
assert!(res.is_err());
193171

194172
// PATCH with missing path_on_host field.
195173
let body = r#"{
196174
"drive_id": "dummy_id"
197175
}"#;
198-
let res = parse_patch_drive(Some(&Body::new(body)), Some(&"dummy_id"));
176+
let res = parse_patch_drive(&Body::new(body), Some(&"dummy_id"));
199177
assert!(res.is_err());
200178

201179
// PATCH with missing drive_id field.
202180
let body = r#"{
203181
"path_on_host": true
204182
}"#;
205-
let res = parse_patch_drive(Some(&Body::new(body)), Some(&"1000"));
183+
let res = parse_patch_drive(&Body::new(body), Some(&"1000"));
206184
assert!(res.is_err());
207185

208186
// PATCH that tries to update something else other than path_on_host.
@@ -211,20 +189,20 @@ mod tests {
211189
"path_on_host": "dummy",
212190
"is_read_only": false
213191
}"#;
214-
let res = parse_patch_drive(Some(&Body::new(body)), Some(&"1234"));
192+
let res = parse_patch_drive(&Body::new(body), Some(&"1234"));
215193
assert!(res.is_err());
216194

217195
// PATCH with payload that is not a json.
218196
let body = r#"{
219197
"fields": "dummy_field"
220198
}"#;
221-
assert!(parse_patch_drive(Some(&Body::new(body)), Some(&"1234")).is_err());
199+
assert!(parse_patch_drive(&Body::new(body), Some(&"1234")).is_err());
222200

223201
let body = r#"{
224202
"drive_id": "foo",
225203
"path_on_host": "dummy"
226204
}"#;
227-
match parse_patch_drive(Some(&Body::new(body)), Some(&"foo")) {
205+
match parse_patch_drive(&Body::new(body), Some(&"foo")) {
228206
Ok(ParsedRequest::Sync(VmmAction::UpdateBlockDevicePath(a, b))) => {
229207
assert_eq!(a, "foo".to_string());
230208
assert_eq!(b, "dummy".to_string());
@@ -237,21 +215,19 @@ mod tests {
237215
"drive_id": "foo",
238216
"path_on_host": "dummy"
239217
}"#;
240-
assert!(parse_patch_drive(Some(&Body::new(body)), Some(&"bar")).is_err());
218+
assert!(parse_patch_drive(&Body::new(body), Some(&"bar")).is_err());
241219
}
242220

243221
#[test]
244222
fn test_parse_put_request() {
245-
assert!(parse_put_drive(None, None).is_err());
246-
assert!(parse_put_drive(None, Some(&"id")).is_err());
247-
assert!(parse_put_drive(Some(&Body::new("invalid_payload")), Some(&"id")).is_err());
223+
assert!(parse_put_drive(&Body::new("invalid_payload"), Some(&"id")).is_err());
248224

249225
// PATCH with invalid fields.
250226
let body = r#"{
251227
"drive_id": "bar",
252228
"is_read_only": false
253229
}"#;
254-
assert!(parse_put_drive(Some(&Body::new(body)), Some(&"2")).is_err());
230+
assert!(parse_put_drive(&Body::new(body), Some(&"2")).is_err());
255231

256232
// PATCH with invalid types on fields. Adding a drive_id as number instead of string.
257233
let body = r#"{
@@ -273,9 +249,9 @@ mod tests {
273249
}
274250
}
275251
}"#;
276-
assert!(parse_put_drive(Some(&Body::new(body)), Some(&"1000")).is_ok());
252+
assert!(parse_put_drive(&Body::new(body), Some(&"1000")).is_ok());
277253

278-
assert!(parse_put_drive(Some(&Body::new(body)), Some(&"foo")).is_err());
254+
assert!(parse_put_drive(&Body::new(body), Some(&"foo")).is_err());
279255
}
280256

281257
#[test]

0 commit comments

Comments
 (0)