Skip to content

Commit

Permalink
Merge pull request #200 from ikatson/decode-torrent
Browse files Browse the repository at this point in the history
[Feature] Decode torrent as JSON in the /torrents/resolve_magnet API
  • Loading branch information
ikatson authored Aug 19, 2024
2 parents 52beec9 + 04cfe9f commit 3d21254
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 3 deletions.
42 changes: 40 additions & 2 deletions crates/bencode/src/bencode_value.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::{collections::HashMap, marker::PhantomData};
use std::{collections::HashMap, fmt::Display, marker::PhantomData};

use buffers::{ByteBuf, ByteBufOwned};
use bytes::Bytes;
use clone_to_owned::CloneToOwned;
use serde::Deserializer;
use serde::{Deserialize, Deserializer, Serialize};

use crate::serde_bencode_de::from_bytes;

Expand Down Expand Up @@ -136,6 +136,44 @@ where
pub type BencodeValueBorrowed<'a> = BencodeValue<ByteBuf<'a>>;
pub type BencodeValueOwned = BencodeValue<ByteBufOwned>;

// A wrapper to deserialize dyn values as strings.
#[derive(PartialEq, Eq, Hash)]
pub struct AsDisplay<T>(T);

impl<'de, T> From<&'de [u8]> for AsDisplay<T>
where
T: From<&'de [u8]>,
{
fn from(value: &'de [u8]) -> Self {
Self(T::from(value))
}
}

impl<T> Serialize for AsDisplay<T>
where
T: Display,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("{}", self.0))
}
}

impl<'de, T> Deserialize<'de> for AsDisplay<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = T::deserialize(deserializer)?;
Ok(AsDisplay(value))
}
}

#[cfg(test)]
mod tests {
use crate::serde_bencode_ser::bencode_serialize_to_writer;
Expand Down
3 changes: 3 additions & 0 deletions crates/buffers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ impl<'a> std::fmt::Display for HexBytes<'a> {
}

fn debug_bytes(b: &[u8], f: &mut std::fmt::Formatter<'_>, debug_strings: bool) -> std::fmt::Result {
if b.is_empty() {
return Ok(());
}
if b.iter().all(|b| *b == 0) {
return write!(f, "<{} bytes, all zeroes>", b.len());
}
Expand Down
19 changes: 18 additions & 1 deletion crates/librqbit/src/http_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use axum::body::Bytes;
use axum::extract::{Path, Query, State};
use axum::response::IntoResponse;
use axum::routing::{get, post};
use bencode::AsDisplay;
use buffers::ByteBuf;
use futures::future::BoxFuture;
use futures::{FutureExt, TryStreamExt};
use http::{HeaderMap, HeaderValue, StatusCode};
Expand Down Expand Up @@ -190,6 +192,7 @@ impl HttpApi {

async fn resolve_magnet(
State(state): State<ApiState>,
inp_headers: HeaderMap,
url: String,
) -> Result<impl IntoResponse> {
let added = state
Expand Down Expand Up @@ -219,7 +222,21 @@ impl HttpApi {
))
}
};

let mut headers = HeaderMap::new();

if inp_headers
.get("Accept")
.and_then(|v| std::str::from_utf8(v.as_bytes()).ok())
== Some("application/json")
{
let data = bencode::dyn_from_bytes::<AsDisplay<ByteBuf>>(&content)
.context("error decoding .torrent file content")?;
let data = serde_json::to_string(&data).context("error serializing")?;
headers.insert("Content-Type", HeaderValue::from_static("application/json"));
return Ok((headers, data).into_response());
}

headers.insert(
"Content-Type",
HeaderValue::from_static("application/x-bittorrent"),
Expand All @@ -234,7 +251,7 @@ impl HttpApi {
}
}
}
Ok((headers, content))
Ok((headers, content).into_response())
}

async fn torrent_playlist(
Expand Down

0 comments on commit 3d21254

Please sign in to comment.