diff --git a/Cargo.lock b/Cargo.lock index a7a6fe1..a3a05a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -811,6 +811,7 @@ dependencies = [ "dht", "futures", "hex 0.4.3", + "http", "librqbit_core", "log", "openssl", diff --git a/crates/librqbit/Cargo.toml b/crates/librqbit/Cargo.toml index e70d0f6..ed0d9db 100644 --- a/crates/librqbit/Cargo.toml +++ b/crates/librqbit/Cargo.toml @@ -31,6 +31,7 @@ serde_json = "1" serde_urlencoded = "*" anyhow = "1" +http = "0.2" regex = "1" reqwest = {version="0.11", default-features=false} urlencoding = "2" diff --git a/crates/librqbit/src/http_api.rs b/crates/librqbit/src/http_api.rs index edd4da7..df1e22b 100644 --- a/crates/librqbit/src/http_api.rs +++ b/crates/librqbit/src/http_api.rs @@ -1,8 +1,8 @@ use anyhow::Context; use axum::extract::{Path, Query, State}; -use axum::http::StatusCode; use buffers::ByteString; use dht::{Dht, DhtStats}; +use http::StatusCode; use librqbit_core::id20::Id20; use librqbit_core::torrent_metainfo::TorrentMetaV1Info; use log::warn; diff --git a/crates/librqbit/src/http_api_client.rs b/crates/librqbit/src/http_api_client.rs index c81eee6..261a484 100644 --- a/crates/librqbit/src/http_api_client.rs +++ b/crates/librqbit/src/http_api_client.rs @@ -19,7 +19,18 @@ async fn check_response(r: reqwest::Response) -> anyhow::Result {}: {}", url, status, body) + + #[derive(Deserialize)] + struct HumanReadableError<'a> { + human_readable: Option<&'a str>, + } + + let human_readable_internal_error = serde_json::from_str::>(&body) + .ok() + .and_then(|e| e.human_readable); + let body_display = human_readable_internal_error.unwrap_or(&body); + + anyhow::bail!("{} -> {}: {}", url, status, body_display) } #[derive(Deserialize)] diff --git a/crates/librqbit/src/http_api_error.rs b/crates/librqbit/src/http_api_error.rs index 07c3633..523e8cb 100644 --- a/crates/librqbit/src/http_api_error.rs +++ b/crates/librqbit/src/http_api_error.rs @@ -1,9 +1,7 @@ use std::ops::Deref; -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, -}; +use axum::response::{IntoResponse, Response}; +use http::StatusCode; use serde::{ser::SerializeMap, Serialize, Serializer}; // Convenience error type. @@ -60,31 +58,40 @@ where } } -impl Serialize for ApiErrorKind { +impl Serialize for ApiError { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - match self { - ApiErrorKind::TorrentNotFound(id) => { - let mut m = serializer.serialize_map(None)?; - m.serialize_entry("error_kind", "torrent_not_found")?; - m.serialize_entry("id", id)?; - m.end() - } - ApiErrorKind::DhtDisabled => { - let mut m = serializer.serialize_map(None)?; - m.serialize_entry("error_kind", "dht_disabled")?; - m.end() - } - ApiErrorKind::Other(err) => { - let mut m = serializer.serialize_map(None)?; - m.serialize_entry("error_kind", "internal_error")?; - m.serialize_entry("human_readable", &format!("{err:#}"))?; - m.serialize_entry("error_chain", &ErrWrap(err.deref()))?; - m.end() - } + #[derive(Serialize, Default)] + struct SerializedError<'a> { + error_kind: &'a str, + human_readable: String, + status: u16, + #[serde(skip_serializing_if = "Option::is_none")] + id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + error_chain: Option>, } + let mut serr: SerializedError = SerializedError { + error_kind: match self.kind { + ApiErrorKind::TorrentNotFound(_) => "torrent_not_found", + ApiErrorKind::DhtDisabled => "dht_disabled", + ApiErrorKind::Other(_) => "internal_error", + }, + human_readable: format!("{self}"), + status: self + .status + .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) + .as_u16(), + ..Default::default() + }; + match &self.kind { + ApiErrorKind::TorrentNotFound(id) => serr.id = Some(*id), + ApiErrorKind::Other(err) => serr.error_chain = Some(ErrWrap(err.deref())), + _ => {} + } + serr.serialize(serializer) } } @@ -118,7 +125,7 @@ impl std::fmt::Display for ApiError { impl IntoResponse for ApiError { fn into_response(self) -> Response { - let mut response = axum::Json(&self.kind).into_response(); + let mut response = axum::Json(&self).into_response(); *response.status_mut() = match self.status { Some(s) => s, None => StatusCode::INTERNAL_SERVER_ERROR,