refactor: deserialize response & request to jsonrpc::{Response,
Request}; warn unwarp_use & expect_use
This commit is contained in:
@@ -23,27 +23,30 @@ pub async fn serve_static(uri: Uri) -> impl IntoResponse {
|
||||
match Assets::get(path.trim_start_matches('/')) {
|
||||
Some(content) => {
|
||||
let mime = mime_guess::from_path(&path).first_or_octet_stream();
|
||||
#[allow(clippy::expect_used)]
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, mime.as_ref())
|
||||
.body(Body::from(content.data))
|
||||
.unwrap()
|
||||
.expect("always Ok")
|
||||
}
|
||||
None => {
|
||||
// For SPA routing, serve index.html for non-asset paths
|
||||
if !path.contains('.')
|
||||
&& let Some(index) = Assets::get("index.html")
|
||||
{
|
||||
#[allow(clippy::expect_used)]
|
||||
return Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(header::CONTENT_TYPE, "text/html")
|
||||
.body(Body::from(index.data))
|
||||
.unwrap();
|
||||
.expect("always Ok");
|
||||
}
|
||||
#[allow(clippy::expect_used)]
|
||||
Response::builder()
|
||||
.status(StatusCode::NOT_FOUND)
|
||||
.body(Body::from("404 Not Found"))
|
||||
.unwrap()
|
||||
.expect("always Ok")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ impl Cryptor {
|
||||
let len = plaintext.len();
|
||||
let mut buffer = plaintext.into_bytes();
|
||||
buffer.extend(std::iter::repeat_n(0, 16 * (len / 16 + 1)));
|
||||
#[allow(clippy::expect_used)]
|
||||
let ciphertext = self
|
||||
.encryptor
|
||||
.clone()
|
||||
|
||||
@@ -139,11 +139,12 @@ pub enum ResponseData {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GenericResponseContent {
|
||||
pub r#type: String,
|
||||
#[serde(default)]
|
||||
pub data: Option<Value>,
|
||||
pub data: Value,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::expect_used)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod test {
|
||||
use indoc::indoc;
|
||||
use serde_json::Map;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#![warn(clippy::unwrap_used)]
|
||||
#![warn(clippy::expect_used)]
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::{
|
||||
self,
|
||||
models::{InterceptionAction, InterceptionRule},
|
||||
},
|
||||
jsonrpc,
|
||||
};
|
||||
|
||||
struct Processed {
|
||||
@@ -113,12 +114,14 @@ pub async fn middleware(
|
||||
|
||||
// Build and return the final response
|
||||
let mut response_builder = Response::builder().status(resp_parts.status);
|
||||
#[allow(clippy::expect_used)]
|
||||
if !resp_parts.headers.is_empty() {
|
||||
*response_builder.headers_mut().unwrap() = resp_parts.headers;
|
||||
*response_builder.headers_mut().expect("always Ok") = resp_parts.headers;
|
||||
}
|
||||
#[allow(clippy::expect_used)]
|
||||
response_builder
|
||||
.body(axum::body::Body::from(processed_resp.encrypted))
|
||||
.unwrap()
|
||||
.expect("always Ok")
|
||||
}
|
||||
|
||||
/// Processes the incoming request body.
|
||||
@@ -132,6 +135,7 @@ async fn process_request(body: &str, ctx: &Arc<AppContext>) -> (Processed, Strin
|
||||
Value::String("Could not deserialize request".into())
|
||||
});
|
||||
decrypt_params(&mut plain_request, ctx);
|
||||
#[allow(clippy::expect_used)]
|
||||
let original = serde_json::to_string(&plain_request).expect("deserialization succeeded");
|
||||
|
||||
let method = plain_request
|
||||
@@ -142,8 +146,9 @@ async fn process_request(body: &str, ctx: &Arc<AppContext>) -> (Processed, Strin
|
||||
""
|
||||
})
|
||||
.to_string();
|
||||
let mut request: jsonrpc::Request = serde_json::from_value(plain_request).unwrap();
|
||||
|
||||
let action = match modify_request(&mut plain_request, &method, ctx).await {
|
||||
let action = match modify_request(&mut request, &method, ctx).await {
|
||||
Ok(action) => action,
|
||||
Err(e) => {
|
||||
warn!("Failed to modify request: {}", e);
|
||||
@@ -152,15 +157,18 @@ async fn process_request(body: &str, ctx: &Arc<AppContext>) -> (Processed, Strin
|
||||
};
|
||||
let final_ = action.map(|action| {
|
||||
(
|
||||
serde_json::to_string(&plain_request).expect("deserialization succeeded"),
|
||||
#[allow(clippy::expect_used)]
|
||||
serde_json::to_string(&request).expect("deserialization succeeded"),
|
||||
action,
|
||||
)
|
||||
});
|
||||
|
||||
let mut encrypted_request = plain_request.clone();
|
||||
#[allow(clippy::expect_used)]
|
||||
let mut encrypted_request = serde_json::to_value(request).expect("deserialization succeeded");
|
||||
encrypt_params(&mut encrypted_request, ctx);
|
||||
|
||||
(
|
||||
#[allow(clippy::expect_used)]
|
||||
Processed {
|
||||
original,
|
||||
final_,
|
||||
@@ -181,14 +189,15 @@ async fn process_response(body: String, method: &str, ctx: &Arc<AppContext>) ->
|
||||
);
|
||||
body
|
||||
});
|
||||
let mut response_value: jsonrpc::Response = serde_json::from_str(&decrypted_body).unwrap();
|
||||
|
||||
let mut response_value: Value = serde_json::from_str(&decrypted_body).unwrap_or_else(|err| {
|
||||
/* let mut response_value: Value = serde_json::from_str(&decrypted_body).unwrap_or_else(|err| {
|
||||
warn!(
|
||||
"Failed to deserialize response body: {}. Using string value.",
|
||||
err
|
||||
);
|
||||
Value::String(decrypted_body.clone())
|
||||
});
|
||||
}); */
|
||||
|
||||
let action = match modify_response(&mut response_value, method, ctx).await {
|
||||
Ok(action) => action,
|
||||
@@ -198,6 +207,7 @@ async fn process_response(body: String, method: &str, ctx: &Arc<AppContext>) ->
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let modified_body_str =
|
||||
serde_json::to_string(&response_value).expect("serialization succeeded");
|
||||
let encrypted = ctx.cryptor.encrypt(modified_body_str.clone());
|
||||
@@ -212,7 +222,7 @@ async fn process_response(body: String, method: &str, ctx: &Arc<AppContext>) ->
|
||||
|
||||
/// Placeholder for request modification logic.
|
||||
async fn modify_request(
|
||||
_request_json: &mut Value,
|
||||
_request: &mut jsonrpc::Request,
|
||||
_method: &str,
|
||||
_ctx: &Arc<AppContext>,
|
||||
) -> anyhow::Result<Option<InterceptionAction>> {
|
||||
@@ -222,19 +232,20 @@ async fn modify_request(
|
||||
|
||||
/// Applies modification rules to the response.
|
||||
async fn modify_response(
|
||||
response_json: &mut Value,
|
||||
response: &mut jsonrpc::Response,
|
||||
method: &str,
|
||||
ctx: &Arc<AppContext>,
|
||||
) -> anyhow::Result<Option<InterceptionAction>> {
|
||||
// Check for generic method interception (e.g., replace response from DB)
|
||||
if let Some((intercepted, action)) = intercept_response(method, ctx).await? {
|
||||
debug!("Intercepting response for method: {}", method);
|
||||
*response_json = serde_json::from_str(&intercepted).unwrap_or_else(|e| {
|
||||
*response = serde_json::from_str(&intercepted).unwrap_or_else(|e| {
|
||||
warn!(
|
||||
"Failed to parse intercepted response as JSON: {}. Using as string.",
|
||||
e
|
||||
);
|
||||
Value::String(intercepted)
|
||||
// Value::String(intercepted)
|
||||
todo!()
|
||||
});
|
||||
return Ok(Some(action));
|
||||
}
|
||||
@@ -242,42 +253,35 @@ async fn modify_response(
|
||||
// Special handling for getcommand
|
||||
// TODO: Return interception rule
|
||||
if method == "com.linspirer.device.getcommand"
|
||||
&& let Err(e) = handle_getcommand_response(response_json, ctx).await
|
||||
&& let jsonrpc::ResponseData::Generic(jsonrpc::GenericResponseContent { data, .. }) =
|
||||
&mut response.data
|
||||
{
|
||||
warn!(
|
||||
"Failed to handle getcommand response: {}. Responding with empty command list.",
|
||||
e
|
||||
);
|
||||
if let Some(obj) = response_json.as_object_mut() {
|
||||
obj.insert("result".to_string(), Value::Array(vec![]));
|
||||
}
|
||||
handle_getcommand_response(data, ctx).await;
|
||||
return Ok(Some(InterceptionAction::Modify));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Handles the 'getcommand' response by injecting verified commands.
|
||||
async fn handle_getcommand_response(
|
||||
response_json: &mut Value,
|
||||
ctx: &Arc<AppContext>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(result) = response_json.get_mut("result")
|
||||
&& let Some(commands) = result.as_array_mut()
|
||||
&& !commands.is_empty()
|
||||
{
|
||||
// Persist commands to database
|
||||
for cmd in commands.iter() {
|
||||
let cmd_json = serde_json::to_string(cmd)?;
|
||||
async fn handle_getcommand_response(data: &mut Value, ctx: &Arc<AppContext>) {
|
||||
if let Some(commands) = data.as_array_mut() {
|
||||
for cmd in commands.drain(..) {
|
||||
let Ok(cmd_json) = serde_json::to_string(&cmd).inspect_err(|err| {
|
||||
warn!("Failed to call serde_json::to_string on {cmd:#?}: {err}");
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
if let Err(e) =
|
||||
crate::db::repositories::commands::insert(&ctx.db, &cmd_json, "unverified").await
|
||||
{
|
||||
warn!("Failed to persist command to database: {}", e);
|
||||
}
|
||||
warn!("Failed to add command to database: {}", e);
|
||||
} else {
|
||||
debug!("Added command to the queue: {:?}", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(obj) = response_json.as_object_mut() {
|
||||
// Get verified commands from database
|
||||
let verified_cmds =
|
||||
match crate::db::repositories::commands::list_by_status(&ctx.db, "verified").await {
|
||||
@@ -294,7 +298,7 @@ async fn handle_getcommand_response(
|
||||
.filter_map(|c| serde_json::from_str(&c.command_json).ok())
|
||||
.collect();
|
||||
|
||||
obj.insert("result".to_string(), Value::Array(verified_values));
|
||||
*data = Value::Array(verified_values);
|
||||
|
||||
// Clear verified commands from database after sending
|
||||
if let Err(e) = crate::db::repositories::commands::clear_verified(&ctx.db).await {
|
||||
@@ -302,9 +306,6 @@ async fn handle_getcommand_response(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks for and applies response interception rules.
|
||||
async fn intercept_response(
|
||||
method: &str,
|
||||
|
||||
@@ -62,12 +62,14 @@ pub async fn proxy_handler(
|
||||
};
|
||||
|
||||
let mut response_builder = Response::builder().status(resp_parts.status);
|
||||
#[allow(clippy::expect_used)]
|
||||
if !resp_parts.headers.is_empty() {
|
||||
*response_builder.headers_mut().unwrap() = resp_parts.headers;
|
||||
*response_builder.headers_mut().expect("always Ok") = resp_parts.headers;
|
||||
}
|
||||
#[allow(clippy::expect_used)]
|
||||
response_builder
|
||||
.body(axum::body::Body::from(resp_body))
|
||||
.unwrap()
|
||||
.expect("always Ok")
|
||||
}
|
||||
|
||||
async fn clone_response(
|
||||
@@ -77,8 +79,9 @@ async fn clone_response(
|
||||
.status(resp.status())
|
||||
.version(resp.version());
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
if !resp.headers().is_empty() {
|
||||
*parts_builder.headers_mut().unwrap() = resp.headers().clone();
|
||||
*parts_builder.headers_mut().expect("always Ok") = resp.headers().clone();
|
||||
}
|
||||
let parts = parts_builder.body(())?.into_parts().0;
|
||||
let body_text = resp.text().await?;
|
||||
|
||||
Reference in New Issue
Block a user