Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop: button to show a modal with logs #48

Merged
merged 3 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/librqbit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ rand = "0.8"
openssl = {version="0.10", optional=true}
crypto-hash = {version="0.3", optional=true}
sha1 = {version = "0.10", optional=true}
tracing-subscriber = {version = "0.3", default-features = false}

uuid = {version = "1.2", features = ["v4"]}
futures = "0.3"
Expand All @@ -65,6 +66,7 @@ dashmap = "5.5.3"
base64 = "0.21.5"
serde_with = "3.4.0"
tokio-util = "0.7.10"
bytes = "1.5.0"

[dev-dependencies]
futures = {version = "0.3"}
Expand Down
27 changes: 25 additions & 2 deletions crates/librqbit/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use std::{net::SocketAddr, sync::Arc};
use anyhow::Context;
use buffers::ByteString;
use dht::{DhtStats, Id20};
use futures::Stream;
use http::StatusCode;
use librqbit_core::torrent_metainfo::TorrentMetaV1Info;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::wrappers::{errors::BroadcastStreamRecvError, BroadcastStream};
use tracing::warn;

use crate::{
Expand All @@ -17,7 +19,7 @@ use crate::{
torrent_state::{
peer::stats::snapshot::{PeerStatsFilter, PeerStatsSnapshot},
ManagedTorrentHandle,
},
}, log_subscriber::LineBroadcast,
};

pub use crate::torrent_state::stats::{LiveStats, TorrentStats};
Expand All @@ -30,13 +32,19 @@ pub type Result<T> = std::result::Result<T, ApiError>;
pub struct Api {
session: Arc<Session>,
rust_log_reload_tx: Option<UnboundedSender<String>>,
line_broadcast: Option<LineBroadcast>,
}

impl Api {
pub fn new(session: Arc<Session>, rust_log_reload_tx: Option<UnboundedSender<String>>) -> Self {
pub fn new(
session: Arc<Session>,
rust_log_reload_tx: Option<UnboundedSender<String>>,
line_broadcast: Option<LineBroadcast>
) -> Self {
Self {
session,
rust_log_reload_tx,
line_broadcast
}
}

Expand Down Expand Up @@ -123,6 +131,21 @@ impl Api {
Ok(Default::default())
}

pub fn api_log_lines_stream(
&self,
) -> Result<
impl Stream<Item = std::result::Result<bytes::Bytes, BroadcastStreamRecvError>>
+ Send
+ Sync
+ 'static,
> {
Ok(self
.line_broadcast
.as_ref()
.map(|sender| BroadcastStream::new(sender.subscribe()))
.context("line_rx wasn't set")?)
}

pub async fn api_add_torrent(
&self,
add: AddTorrent<'_>,
Expand Down
64 changes: 36 additions & 28 deletions crates/librqbit/src/http_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,43 @@ use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::UnboundedSender;
use tracing::info;
use tracing::{info, warn};

use axum::Router;

use crate::api::Api;
use crate::peer_connection::PeerConnectionOptions;
use crate::session::{AddTorrent, AddTorrentOptions, Session, SUPPORTED_SCHEMES};
use crate::session::{AddTorrent, AddTorrentOptions, SUPPORTED_SCHEMES};
use crate::torrent_state::peer::stats::snapshot::PeerStatsFilter;

type ApiState = Arc<Api>;
type ApiState = Api;

use crate::api::Result;

/// An HTTP server for the API.
#[derive(Clone)]
pub struct HttpApi {
inner: ApiState,
opts: HttpApiOptions,
}

#[derive(Debug, Default)]
pub struct HttpApiOptions {
pub cors_enable_all: bool,
pub read_only: bool,
}

impl HttpApi {
pub fn new(session: Arc<Session>, rust_log_reload_tx: Option<UnboundedSender<String>>) -> Self {
pub fn new(api: Api, opts: Option<HttpApiOptions>) -> Self {
Self {
inner: Arc::new(Api::new(session, rust_log_reload_tx)),
inner: api,
opts: opts.unwrap_or_default(),
}
}

/// Run the HTTP server forever on the given address.
/// If read_only is passed, no state-modifying methods will be exposed.
pub async fn make_http_api_and_run(
self,
addr: SocketAddr,
read_only: bool,
) -> anyhow::Result<()> {
pub async fn make_http_api_and_run(self, addr: SocketAddr) -> anyhow::Result<()> {
let state = self.inner;

async fn api_root() -> impl IntoResponse {
Expand Down Expand Up @@ -185,8 +186,14 @@ impl HttpApi {
state.api_set_rust_log(new_value).map(axum::Json)
}

async fn stream_logs(State(state): State<ApiState>) -> Result<impl IntoResponse> {
let s = state.api_log_lines_stream()?;
Ok(axum::body::Body::from_stream(s))
}

let mut app = Router::new()
.route("/", get(api_root))
.route("/stream_logs", get(stream_logs))
.route("/rust_log", post(set_rust_log))
.route("/dht/stats", get(dht_stats))
.route("/dht/table", get(dht_table))
Expand All @@ -197,7 +204,7 @@ impl HttpApi {
.route("/torrents/:id/stats/v1", get(torrent_stats_v1))
.route("/torrents/:id/peer_stats", get(peer_stats));

if !read_only {
if !self.opts.read_only {
app = app
.route("/torrents", post(torrents_post))
.route("/torrents/:id/pause", post(torrent_action_pause))
Expand All @@ -208,7 +215,6 @@ impl HttpApi {

#[cfg(feature = "webui")]
{
use tracing::warn;
let webui_router = Router::new()
.route(
"/",
Expand Down Expand Up @@ -238,23 +244,25 @@ impl HttpApi {
}),
);

// This is to develop webui by just doing "open index.html && tsc --watch"
let cors_layer = std::env::var("CORS_DEBUG")
.ok()
.map(|_| {
use tower_http::cors::{AllowHeaders, AllowOrigin};
app = app.nest("/web/", webui_router);
}

warn!("CorsLayer: allowing everything because CORS_DEBUG is set");
tower_http::cors::CorsLayer::default()
.allow_origin(AllowOrigin::predicate(|_, _| true))
.allow_headers(AllowHeaders::any())
})
.unwrap_or_default();
let enable_cors = std::env::var("CORS_DEBUG").is_ok() || self.opts.cors_enable_all;

app = app.nest("/web/", webui_router).layer(cors_layer);
}
// This is to develop webui by just doing "open index.html && tsc --watch"
let cors_layer = if enable_cors {
use tower_http::cors::{AllowHeaders, AllowOrigin};

warn!("CorsLayer: allowing everything");
tower_http::cors::CorsLayer::default()
.allow_origin(AllowOrigin::predicate(|_, _| true))
.allow_headers(AllowHeaders::any())
} else {
Default::default()
};

let app = app
.layer(cors_layer)
.layer(tower_http::trace::TraceLayer::new_for_http())
.with_state(state)
.into_make_service();
Expand Down
1 change: 1 addition & 0 deletions crates/librqbit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod dht_utils;
mod file_ops;
pub mod http_api;
pub mod http_api_client;
pub mod log_subscriber;
mod peer_connection;
mod peer_info_reader;
mod session;
Expand Down
47 changes: 47 additions & 0 deletions crates/librqbit/src/log_subscriber.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::io::LineWriter;

use bytes::Bytes;
use tracing_subscriber::fmt::MakeWriter;

pub struct Subscriber {
tx: tokio::sync::broadcast::Sender<Bytes>,
}

pub struct Writer {
tx: tokio::sync::broadcast::Sender<Bytes>,
}

pub type LineBroadcast = tokio::sync::broadcast::Sender<Bytes>;

impl Subscriber {
pub fn new() -> (Self, LineBroadcast) {
let (tx, _) = tokio::sync::broadcast::channel(100);
(Self { tx: tx.clone() }, tx)
}
}

impl<'a> MakeWriter<'a> for Subscriber {
type Writer = LineWriter<Writer>;

fn make_writer(&self) -> Self::Writer {
LineWriter::new(Writer {
tx: self.tx.clone(),
})
}
}

impl std::io::Write for Writer {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let len = buf.len();
if self.tx.receiver_count() == 0 {
return Ok(len);
}
let arc = buf.to_vec().into();
let _ = self.tx.send(arc);
Ok(len)
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
1 change: 1 addition & 0 deletions crates/librqbit/webui/src/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export interface AddTorrentOptions {
}

export interface RqbitAPI {
getHttpBaseUrl: () => string | null;
listTorrents: () => Promise<ListTorrentsResponse>;
getTorrentDetails: (index: number) => Promise<TorrentDetails>;
getTorrentStats: (index: number) => Promise<TorrentStats>;
Expand Down
Loading
Loading