add error codes to control protocol
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
0f821068e5b16b1e52b9c9bc14ddebded4834d73- Parents
-
33bdd6d - Tree
4c1a3d9
0f82106
0f821068e5b16b1e52b9c9bc14ddebded4834d7333bdd6d
4c1a3d9| Status | File | + | - |
|---|---|---|---|
| M |
garwarp-ipc/src/lib.rs
|
28 | 9 |
| M |
garwarp/src/daemon.rs
|
7 | 0 |
| M |
garwarpctl/src/main.rs
|
12 | 12 |
garwarp-ipc/src/lib.rsmodified@@ -193,7 +193,7 @@ pub enum ControlResponse { | ||
| 193 | 193 | Status(StatusResponse), |
| 194 | 194 | AckStopping, |
| 195 | 195 | AckRequest { id: String, state: String }, |
| 196 | - Error { reason: String }, | |
| 196 | + Error { code: u32, reason: String }, | |
| 197 | 197 | } |
| 198 | 198 | |
| 199 | 199 | impl ControlResponse { |
@@ -210,7 +210,7 @@ impl ControlResponse { | ||
| 210 | 210 | Self::AckRequest { id, state } => { |
| 211 | 211 | format!("ack request id={} state={}\n", id, state) |
| 212 | 212 | } |
| 213 | - Self::Error { reason } => format!("error reason={}\n", reason), | |
| 213 | + Self::Error { code, reason } => format!("error code={} reason={}\n", code, reason), | |
| 214 | 214 | } |
| 215 | 215 | } |
| 216 | 216 | |
@@ -286,15 +286,30 @@ impl ControlResponse { | ||
| 286 | 286 | None => Err(ParseError::MissingField("ack")), |
| 287 | 287 | }, |
| 288 | 288 | Some("error") => match parts.next() { |
| 289 | - Some(reason_field) => { | |
| 290 | - let (key, value) = reason_field | |
| 291 | - .split_once('=') | |
| 292 | - .ok_or(ParseError::InvalidField(reason_field.to_string()))?; | |
| 293 | - if key != "reason" { | |
| 294 | - return Err(ParseError::InvalidField(reason_field.to_string())); | |
| 289 | + Some(first_field) => { | |
| 290 | + let mut code = None; | |
| 291 | + let mut reason = None; | |
| 292 | + let mut fields = vec![first_field]; | |
| 293 | + fields.extend(parts); | |
| 294 | + | |
| 295 | + for field in fields { | |
| 296 | + let (key, value) = field | |
| 297 | + .split_once('=') | |
| 298 | + .ok_or(ParseError::InvalidField(field.to_string()))?; | |
| 299 | + match key { | |
| 300 | + "code" => { | |
| 301 | + code = | |
| 302 | + Some(value.parse::<u32>().map_err(|_| { | |
| 303 | + ParseError::InvalidField(field.to_string()) | |
| 304 | + })?); | |
| 305 | + } | |
| 306 | + "reason" => reason = Some(value.to_string()), | |
| 307 | + _ => return Err(ParseError::InvalidField(field.to_string())), | |
| 308 | + } | |
| 295 | 309 | } |
| 296 | 310 | Ok(Self::Error { |
| 297 | - reason: value.to_string(), | |
| 311 | + code: code.ok_or(ParseError::MissingField("code"))?, | |
| 312 | + reason: reason.ok_or(ParseError::MissingField("reason"))?, | |
| 298 | 313 | }) |
| 299 | 314 | } |
| 300 | 315 | None => Err(ParseError::MissingField("reason")), |
@@ -389,6 +404,10 @@ mod tests { | ||
| 389 | 404 | id: "req-1".to_string(), |
| 390 | 405 | state: "pending".to_string(), |
| 391 | 406 | }, |
| 407 | + ControlResponse::Error { | |
| 408 | + code: 2, | |
| 409 | + reason: "invalid_request".to_string(), | |
| 410 | + }, | |
| 392 | 411 | ] { |
| 393 | 412 | let line = response.to_line(); |
| 394 | 413 | let parsed = ControlResponse::parse_line(&line).expect("response should parse"); |
garwarp/src/daemon.rsmodified@@ -127,6 +127,7 @@ fn handle_connection(stream: UnixStream, state: &mut DaemonState) -> io::Result< | ||
| 127 | 127 | return write_response( |
| 128 | 128 | reader.into_inner(), |
| 129 | 129 | ControlResponse::Error { |
| 130 | + code: mapping.code as u32, | |
| 130 | 131 | reason: mapping.reason.to_string(), |
| 131 | 132 | }, |
| 132 | 133 | ); |
@@ -144,6 +145,7 @@ fn handle_connection(stream: UnixStream, state: &mut DaemonState) -> io::Result< | ||
| 144 | 145 | Err(error) => { |
| 145 | 146 | let mapping = map_request_error(&error); |
| 146 | 147 | ControlResponse::Error { |
| 148 | + code: mapping.code as u32, | |
| 147 | 149 | reason: mapping.reason.to_string(), |
| 148 | 150 | } |
| 149 | 151 | } |
@@ -165,6 +167,7 @@ fn handle_connection(stream: UnixStream, state: &mut DaemonState) -> io::Result< | ||
| 165 | 167 | Err(error) => { |
| 166 | 168 | let mapping = map_request_error(&error); |
| 167 | 169 | ControlResponse::Error { |
| 170 | + code: mapping.code as u32, | |
| 168 | 171 | reason: mapping.reason.to_string(), |
| 169 | 172 | } |
| 170 | 173 | } |
@@ -173,6 +176,7 @@ fn handle_connection(stream: UnixStream, state: &mut DaemonState) -> io::Result< | ||
| 173 | 176 | None => { |
| 174 | 177 | let mapping = map_portal_error(&PortalError::InvalidRequestPayload); |
| 175 | 178 | ControlResponse::Error { |
| 179 | + code: mapping.code as u32, | |
| 176 | 180 | reason: mapping.reason.to_string(), |
| 177 | 181 | } |
| 178 | 182 | } |
@@ -336,6 +340,7 @@ mod tests { | ||
| 336 | 340 | assert_eq!( |
| 337 | 341 | response, |
| 338 | 342 | ControlResponse::Error { |
| 343 | + code: 2, | |
| 339 | 344 | reason: "invalid_request".to_string(), |
| 340 | 345 | } |
| 341 | 346 | ); |
@@ -399,6 +404,7 @@ mod tests { | ||
| 399 | 404 | assert_eq!( |
| 400 | 405 | response, |
| 401 | 406 | ControlResponse::Error { |
| 407 | + code: 2, | |
| 402 | 408 | reason: "invalid_parent_window".to_string(), |
| 403 | 409 | } |
| 404 | 410 | ); |
@@ -436,6 +442,7 @@ mod tests { | ||
| 436 | 442 | assert_eq!( |
| 437 | 443 | response, |
| 438 | 444 | ControlResponse::Error { |
| 445 | + code: 2, | |
| 439 | 446 | reason: "ownership_mismatch".to_string(), |
| 440 | 447 | } |
| 441 | 448 | ); |
garwarpctl/src/main.rsmodified@@ -169,9 +169,9 @@ fn run(command: Command) -> io::Result<()> { | ||
| 169 | 169 | println!("in_flight={}", status.in_flight_requests); |
| 170 | 170 | Ok(()) |
| 171 | 171 | } |
| 172 | - ControlResponse::Error { reason } => { | |
| 173 | - Err(io::Error::other(format!("daemon error: {reason}"))) | |
| 174 | - } | |
| 172 | + ControlResponse::Error { code, reason } => Err(io::Error::other(format!( | |
| 173 | + "daemon error: code={code} reason={reason}" | |
| 174 | + ))), | |
| 175 | 175 | other => Err(io::Error::new( |
| 176 | 176 | io::ErrorKind::InvalidData, |
| 177 | 177 | format!("unexpected response: {other:?}"), |
@@ -185,9 +185,9 @@ fn run(command: Command) -> io::Result<()> { | ||
| 185 | 185 | println!("stopping"); |
| 186 | 186 | Ok(()) |
| 187 | 187 | } |
| 188 | - ControlResponse::Error { reason } => { | |
| 189 | - Err(io::Error::other(format!("daemon error: {reason}"))) | |
| 190 | - } | |
| 188 | + ControlResponse::Error { code, reason } => Err(io::Error::other(format!( | |
| 189 | + "daemon error: code={code} reason={reason}" | |
| 190 | + ))), | |
| 191 | 191 | other => Err(io::Error::new( |
| 192 | 192 | io::ErrorKind::InvalidData, |
| 193 | 193 | format!("unexpected response: {other:?}"), |
@@ -212,9 +212,9 @@ fn run(command: Command) -> io::Result<()> { | ||
| 212 | 212 | println!("state={state}"); |
| 213 | 213 | Ok(()) |
| 214 | 214 | } |
| 215 | - ControlResponse::Error { reason } => { | |
| 216 | - Err(io::Error::other(format!("daemon error: {reason}"))) | |
| 217 | - } | |
| 215 | + ControlResponse::Error { code, reason } => Err(io::Error::other(format!( | |
| 216 | + "daemon error: code={code} reason={reason}" | |
| 217 | + ))), | |
| 218 | 218 | other => Err(io::Error::new( |
| 219 | 219 | io::ErrorKind::InvalidData, |
| 220 | 220 | format!("unexpected response: {other:?}"), |
@@ -239,9 +239,9 @@ fn run(command: Command) -> io::Result<()> { | ||
| 239 | 239 | println!("state={state}"); |
| 240 | 240 | Ok(()) |
| 241 | 241 | } |
| 242 | - ControlResponse::Error { reason } => { | |
| 243 | - Err(io::Error::other(format!("daemon error: {reason}"))) | |
| 244 | - } | |
| 242 | + ControlResponse::Error { code, reason } => Err(io::Error::other(format!( | |
| 243 | + "daemon error: code={code} reason={reason}" | |
| 244 | + ))), | |
| 245 | 245 | other => Err(io::Error::new( |
| 246 | 246 | io::ErrorKind::InvalidData, |
| 247 | 247 | format!("unexpected response: {other:?}"), |