HTTP API: move functions out to global scope

This commit is contained in:
Igor Katson 2025-01-14 09:50:18 +00:00
parent e79b51ea1f
commit c8d4c8d713
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5

View file

@ -144,67 +144,6 @@ mod timeout {
use timeout::Timeout; use timeout::Timeout;
impl HttpApi {
pub fn new(api: Api, opts: Option<HttpApiOptions>) -> Self {
Self {
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.
#[inline(never)]
pub fn make_http_api_and_run(
mut self,
listener: TcpListener,
upnp_router: Option<Router>,
) -> BoxFuture<'static, anyhow::Result<()>> {
let state = self.inner;
let api_root = move |parts: Parts| async move {
// If browser, and webui enabled, redirect to web
#[cfg(feature = "webui")]
{
if parts.headers.get("Accept").map_or(false, |h| {
std::str::from_utf8(h.as_bytes()).map_or(false, |s| s.contains("text/html"))
}) {
return Redirect::temporary("./web/").into_response();
}
}
(
[("Content-Type", "application/json")],
axum::Json(serde_json::json!({
"apis": {
"GET /": "list all available APIs",
"GET /dht/stats": "DHT stats",
"GET /dht/table": "DHT routing table",
"GET /torrents": "List torrents",
"GET /torrents/playlist": "Generate M3U8 playlist for all files in all torrents",
"GET /stats": "Global session stats",
"POST /torrents/resolve_magnet": "Resolve a magnet to torrent file bytes",
"GET /torrents/{id_or_infohash}": "Torrent details",
"GET /torrents/{id_or_infohash}/haves": "The bitfield of have pieces",
"GET /torrents/{id_or_infohash}/playlist": "Generate M3U8 playlist for this torrent",
"GET /torrents/{id_or_infohash}/stats/v1": "Torrent stats",
"GET /torrents/{id_or_infohash}/peer_stats": "Per peer stats",
"GET /torrents/{id_or_infohash}/stream/{file_idx}": "Stream a file. Accepts Range header to seek.",
"POST /torrents/{id_or_infohash}/pause": "Pause torrent",
"POST /torrents/{id_or_infohash}/start": "Resume torrent",
"POST /torrents/{id_or_infohash}/forget": "Forget about the torrent, keep the files",
"POST /torrents/{id_or_infohash}/delete": "Forget about the torrent, remove the files",
"POST /torrents/{id_or_infohash}/update_only_files": "Change the selection of files to download. You need to POST json of the following form {\"only_files\": [0, 1, 2]}",
"POST /torrents": "Add a torrent here. magnet: or http:// or a local file.",
"POST /rust_log": "Set RUST_LOG to this post launch (for debugging)",
"GET /web/": "Web UI",
},
"server": "rqbit",
"version": env!("CARGO_PKG_VERSION"),
})),
).into_response()
};
async fn dht_stats(State(state): State<ApiState>) -> Result<impl IntoResponse> { async fn dht_stats(State(state): State<ApiState>) -> Result<impl IntoResponse> {
state.api_dht_stats().map(axum::Json) state.api_dht_stats().map(axum::Json)
} }
@ -307,9 +246,7 @@ impl HttpApi {
fn get_host(headers: &HeaderMap) -> Result<&str> { fn get_host(headers: &HeaderMap) -> Result<&str> {
Ok(headers Ok(headers
.get("host") .get("host")
.ok_or_else(|| { .ok_or_else(|| ApiError::new_from_text(StatusCode::BAD_REQUEST, "Missing host header"))?
ApiError::new_from_text(StatusCode::BAD_REQUEST, "Missing host header")
})?
.to_str() .to_str()
.context("hostname is not string")?) .context("hostname is not string")?)
} }
@ -633,6 +570,67 @@ impl HttpApi {
Ok(Json(EmptyJsonResponse {})) Ok(Json(EmptyJsonResponse {}))
} }
impl HttpApi {
pub fn new(api: Api, opts: Option<HttpApiOptions>) -> Self {
Self {
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.
#[inline(never)]
pub fn make_http_api_and_run(
mut self,
listener: TcpListener,
upnp_router: Option<Router>,
) -> BoxFuture<'static, anyhow::Result<()>> {
let state = self.inner;
let api_root = move |parts: Parts| async move {
// If browser, and webui enabled, redirect to web
#[cfg(feature = "webui")]
{
if parts.headers.get("Accept").map_or(false, |h| {
std::str::from_utf8(h.as_bytes()).map_or(false, |s| s.contains("text/html"))
}) {
return Redirect::temporary("./web/").into_response();
}
}
(
[("Content-Type", "application/json")],
axum::Json(serde_json::json!({
"apis": {
"GET /": "list all available APIs",
"GET /dht/stats": "DHT stats",
"GET /dht/table": "DHT routing table",
"GET /torrents": "List torrents",
"GET /torrents/playlist": "Generate M3U8 playlist for all files in all torrents",
"GET /stats": "Global session stats",
"POST /torrents/resolve_magnet": "Resolve a magnet to torrent file bytes",
"GET /torrents/{id_or_infohash}": "Torrent details",
"GET /torrents/{id_or_infohash}/haves": "The bitfield of have pieces",
"GET /torrents/{id_or_infohash}/playlist": "Generate M3U8 playlist for this torrent",
"GET /torrents/{id_or_infohash}/stats/v1": "Torrent stats",
"GET /torrents/{id_or_infohash}/peer_stats": "Per peer stats",
"GET /torrents/{id_or_infohash}/stream/{file_idx}": "Stream a file. Accepts Range header to seek.",
"POST /torrents/{id_or_infohash}/pause": "Pause torrent",
"POST /torrents/{id_or_infohash}/start": "Resume torrent",
"POST /torrents/{id_or_infohash}/forget": "Forget about the torrent, keep the files",
"POST /torrents/{id_or_infohash}/delete": "Forget about the torrent, remove the files",
"POST /torrents/{id_or_infohash}/update_only_files": "Change the selection of files to download. You need to POST json of the following form {\"only_files\": [0, 1, 2]}",
"POST /torrents": "Add a torrent here. magnet: or http:// or a local file.",
"POST /rust_log": "Set RUST_LOG to this post launch (for debugging)",
"GET /web/": "Web UI",
},
"server": "rqbit",
"version": env!("CARGO_PKG_VERSION"),
})),
).into_response()
};
let mut app = Router::new() let mut app = Router::new()
.route("/", get(api_root)) .route("/", get(api_root))
.route("/stream_logs", get(stream_logs)) .route("/stream_logs", get(stream_logs))