#![warn(clippy::pedantic, clippy::nursery, clippy::all, clippy::cargo)]
#![allow(clippy::multiple_crate_versions, clippy::module_name_repetitions)]
use std::collections::HashMap;
use std::sync::LazyLock;
use anyhow::Result;
use arti_client::config::TorClientConfigBuilder;
use arti_client::{TorClient, TorClientConfig};
use error::Error;
use make_request::MakeRequest;
use make_request::{make_local_request, make_request};
pub use response::Response;
pub(crate) use response::{UpstreamRequest, UpstreamResponse};
use streams::{create_http_stream, https_upgrade};
use tokio::sync::Mutex as TokioMutex;
use tor_client::get_or_refresh;
use tor_rtcompat::PreferredRuntime;
use uri::parse_uri;
use uri::Uri;
mod error;
mod make_request;
mod response;
mod streams;
mod tor_client;
mod uri;
static TOR_CONFIG: LazyLock<TorClientConfig> = LazyLock::new(|| {
let mut default_config = TorClientConfigBuilder::default();
default_config.address_filter().allow_onion_addrs(true);
default_config.build().unwrap()
});
static TOR_CLIENT: LazyLock<TokioMutex<Option<TorClient<PreferredRuntime>>>> = LazyLock::new(|| TokioMutex::new(None));
pub async fn get(uri: &str) -> Result<Response> {
let uri = parse_uri(uri)?;
let m_r = MakeRequest { uri: uri.clone(), headers: Option::default(), body: Option::default(), method: hyper::Method::GET, version: hyper::Version::HTTP_2 };
if uri.is_local {
return make_local_request(m_r).await;
}
let stream = create_http_stream(&uri, 5).await?;
if uri.is_https {
let stream = https_upgrade(&uri, stream).await?;
make_request(m_r, stream).await
} else {
make_request(m_r, stream).await
}
}
pub async fn post(uri: &str, body: &str, headers: Option<Vec<(&str, &str)>>) -> Result<Response> {
let uri = parse_uri(uri)?;
let headers = headers.unwrap_or_default();
let headers: HashMap<_, _> = headers.into_iter().collect();
let m_r = MakeRequest { uri: uri.clone(), headers: Some(headers), body: Some(body.to_string()), method: hyper::Method::POST, version: hyper::Version::HTTP_2 };
if uri.is_local {
return make_local_request(m_r).await;
}
let stream = create_http_stream(&uri, 5).await?;
if uri.is_https {
let stream = https_upgrade(&uri, stream).await?;
make_request(m_r, stream).await
} else {
make_request(m_r, stream).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_get() {
let response = get("http://vpns6exmqmg5znqmgxa5c6rgzpt6imy5yzrbsoszovgfipdjypnchpyd.onion/status").await.unwrap();
println!("body: {}", response);
assert!(response.to_string().contains("ok"));
let response = get("https://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion").await.unwrap();
println!("body: {}", response);
if !response.to_string().is_empty() {
assert!(response.to_string().contains("facebook"));
}
let response = match get("http://localhost:8225/status").await {
Ok(r) => r,
Err(e) => {
println!("error: {}", e);
return;
}
};
println!("body: {}", response);
assert!(response.to_string().contains("ok"));
}
#[tokio::test]
async fn test_post() {
let post_body = r#"{"test":"testing"}"#;
let response = post("http://vpns6exmqmg5znqmgxa5c6rgzpt6imy5yzrbsoszovgfipdjypnchpyd.onion/echo", post_body, None).await.unwrap();
println!("body: {}", response);
assert!(response.to_string().contains("test"));
let post_body = r#"{"test":"test"}"#;
let body = post("https://echo.free.beeceptor.com", post_body, None).await.unwrap();
println!("body: {}", body);
assert!(body.to_string().contains("test"));
let post_body = r#"{"test":"testing"}"#;
let response = match post("http://localhost:8225/echo", post_body, None).await {
Ok(r) => r,
Err(e) => {
println!("error: {}", e);
return;
}
};
println!("body: {}", response);
assert!(response.to_string().contains("test"));
}
}