mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-04-01 18:25:30 +00:00
http_private: get basic announce route working
This commit is contained in:
parent
b0f89edd30
commit
96e128bb90
7 changed files with 181 additions and 36 deletions
48
aquatic_http_private/src/common.rs
Normal file
48
aquatic_http_private/src/common.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use tokio::sync::{mpsc, oneshot};
|
||||||
|
|
||||||
|
use aquatic_common::CanonicalSocketAddr;
|
||||||
|
use aquatic_http_protocol::{common::InfoHash, response::Response};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::Config,
|
||||||
|
workers::{common::ChannelAnnounceRequest, socket::db::ValidatedAnnounceRequest},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct RequestWorkerIndex(pub usize);
|
||||||
|
|
||||||
|
impl RequestWorkerIndex {
|
||||||
|
pub fn from_info_hash(config: &Config, info_hash: InfoHash) -> Self {
|
||||||
|
Self(info_hash.0[0] as usize % config.request_workers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChannelRequestSender(Vec<mpsc::Sender<ChannelAnnounceRequest>>);
|
||||||
|
|
||||||
|
impl ChannelRequestSender {
|
||||||
|
pub fn new(senders: Vec<mpsc::Sender<ChannelAnnounceRequest>>) -> Self {
|
||||||
|
Self(senders)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_to(
|
||||||
|
&self,
|
||||||
|
index: RequestWorkerIndex,
|
||||||
|
request: ValidatedAnnounceRequest,
|
||||||
|
source_addr: CanonicalSocketAddr,
|
||||||
|
) -> anyhow::Result<oneshot::Receiver<Response>> {
|
||||||
|
let (response_sender, response_receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
let request = ChannelAnnounceRequest {
|
||||||
|
request,
|
||||||
|
source_addr,
|
||||||
|
response_sender,
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.0[index.0].send(request).await {
|
||||||
|
Ok(()) => Ok(response_receiver),
|
||||||
|
Err(err) => {
|
||||||
|
Err(anyhow::Error::new(err).context("error sending ChannelAnnounceRequest"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ pub struct Config {
|
||||||
/// Request workers receive a number of requests from socket workers,
|
/// Request workers receive a number of requests from socket workers,
|
||||||
/// generate responses and send them back to the socket workers.
|
/// generate responses and send them back to the socket workers.
|
||||||
pub request_workers: usize,
|
pub request_workers: usize,
|
||||||
|
pub worker_channel_size: usize,
|
||||||
pub log_level: LogLevel,
|
pub log_level: LogLevel,
|
||||||
pub network: NetworkConfig,
|
pub network: NetworkConfig,
|
||||||
pub protocol: ProtocolConfig,
|
pub protocol: ProtocolConfig,
|
||||||
|
|
@ -29,6 +30,7 @@ impl Default for Config {
|
||||||
Self {
|
Self {
|
||||||
socket_workers: 1,
|
socket_workers: 1,
|
||||||
request_workers: 1,
|
request_workers: 1,
|
||||||
|
worker_channel_size: 128,
|
||||||
log_level: LogLevel::default(),
|
log_level: LogLevel::default(),
|
||||||
network: NetworkConfig::default(),
|
network: NetworkConfig::default(),
|
||||||
protocol: ProtocolConfig::default(),
|
protocol: ProtocolConfig::default(),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
|
mod common;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
mod workers;
|
mod workers;
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use common::ChannelRequestSender;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
|
use tokio::sync::mpsc::channel;
|
||||||
|
|
||||||
pub const APP_NAME: &str = "aquatic_http_private: private HTTP/TLS BitTorrent tracker";
|
pub const APP_NAME: &str = "aquatic_http_private: private HTTP/TLS BitTorrent tracker";
|
||||||
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
@ -9,24 +14,36 @@ pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub fn run(config: config::Config) -> anyhow::Result<()> {
|
pub fn run(config: config::Config) -> anyhow::Result<()> {
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
|
|
||||||
|
let mut request_senders = Vec::new();
|
||||||
|
let mut request_receivers = VecDeque::new();
|
||||||
|
|
||||||
|
for _ in 0..config.request_workers {
|
||||||
|
let (request_sender, request_receiver) = channel(config.worker_channel_size);
|
||||||
|
|
||||||
|
request_senders.push(request_sender);
|
||||||
|
request_receivers.push_back(request_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
|
|
||||||
for _ in 0..config.socket_workers {
|
for _ in 0..config.socket_workers {
|
||||||
let config = config.clone();
|
let config = config.clone();
|
||||||
|
let request_sender = ChannelRequestSender::new(request_senders.clone());
|
||||||
|
|
||||||
let handle = ::std::thread::Builder::new()
|
let handle = ::std::thread::Builder::new()
|
||||||
.name("socket".into())
|
.name("socket".into())
|
||||||
.spawn(move || workers::socket::run_socket_worker(config))?;
|
.spawn(move || workers::socket::run_socket_worker(config, request_sender))?;
|
||||||
|
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..config.request_workers {
|
for _ in 0..config.request_workers {
|
||||||
let config = config.clone();
|
let config = config.clone();
|
||||||
|
let request_receiver = request_receivers.pop_front().unwrap();
|
||||||
|
|
||||||
let handle = ::std::thread::Builder::new()
|
let handle = ::std::thread::Builder::new()
|
||||||
.name("request".into())
|
.name("request".into())
|
||||||
.spawn(move || workers::request::run_request_worker(config))?;
|
.spawn(move || workers::request::run_request_worker(config, request_receiver))?;
|
||||||
|
|
||||||
handles.push(handle);
|
handles.push(handle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use aquatic_common::CanonicalSocketAddr;
|
use aquatic_common::CanonicalSocketAddr;
|
||||||
use aquatic_http_protocol::response::AnnounceResponse;
|
use aquatic_http_protocol::response::Response;
|
||||||
use tokio::sync::oneshot::Sender;
|
use tokio::sync::oneshot::Sender;
|
||||||
|
|
||||||
use super::socket::db::ValidatedAnnounceRequest;
|
use super::socket::db::ValidatedAnnounceRequest;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ChannelAnnounceRequest {
|
pub struct ChannelAnnounceRequest {
|
||||||
pub request: ValidatedAnnounceRequest,
|
pub request: ValidatedAnnounceRequest,
|
||||||
pub source_addr: CanonicalSocketAddr,
|
pub source_addr: CanonicalSocketAddr,
|
||||||
pub response_sender: Sender<AnnounceResponse>,
|
pub response_sender: Sender<Response>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,40 @@
|
||||||
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
|
use aquatic_http_protocol::response::{FailureResponse, Response};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
pub fn run_request_worker(config: Config) -> anyhow::Result<()> {
|
use super::common::ChannelAnnounceRequest;
|
||||||
|
|
||||||
|
pub fn run_request_worker(
|
||||||
|
config: Config,
|
||||||
|
request_receiver: Receiver<ChannelAnnounceRequest>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
runtime.block_on(run_inner(config, request_receiver))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn run_inner(
|
||||||
|
config: Config,
|
||||||
|
mut request_receiver: Receiver<ChannelAnnounceRequest>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
loop {
|
||||||
|
let request = request_receiver
|
||||||
|
.recv()
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("request channel closed"))?;
|
||||||
|
|
||||||
|
println!("{:?}", request);
|
||||||
|
|
||||||
|
let _ = request
|
||||||
|
.response_sender
|
||||||
|
.send(Response::Failure(FailureResponse::new(
|
||||||
|
"successful actually",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,37 @@
|
||||||
pub mod db;
|
pub mod db;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
use std::net::{SocketAddr, TcpListener};
|
use std::{
|
||||||
|
net::{SocketAddr, TcpListener},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use axum::{routing::get, Extension, Router};
|
use axum::{routing::get, Extension, Router};
|
||||||
use sqlx::mysql::MySqlPoolOptions;
|
use sqlx::mysql::MySqlPoolOptions;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::{common::ChannelRequestSender, config::Config};
|
||||||
|
|
||||||
pub fn run_socket_worker(config: Config) -> anyhow::Result<()> {
|
pub fn run_socket_worker(
|
||||||
|
config: Config,
|
||||||
|
request_sender: ChannelRequestSender,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let tcp_listener = create_tcp_listener(config.network.address)?;
|
let tcp_listener = create_tcp_listener(config.network.address)?;
|
||||||
|
|
||||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
runtime.block_on(run_app(tcp_listener))?;
|
runtime.block_on(run_app(config, tcp_listener, request_sender))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_app(tcp_listener: TcpListener) -> anyhow::Result<()> {
|
async fn run_app(
|
||||||
|
config: Config,
|
||||||
|
tcp_listener: TcpListener,
|
||||||
|
request_sender: ChannelRequestSender,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let db_url = ::std::env::var("DATABASE_URL").expect("env var DATABASE_URL");
|
let db_url = ::std::env::var("DATABASE_URL").expect("env var DATABASE_URL");
|
||||||
|
|
||||||
let pool = MySqlPoolOptions::new()
|
let pool = MySqlPoolOptions::new()
|
||||||
|
|
@ -31,7 +41,9 @@ async fn run_app(tcp_listener: TcpListener) -> anyhow::Result<()> {
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/:user_token/announce/", get(routes::announce))
|
.route("/:user_token/announce/", get(routes::announce))
|
||||||
.layer(Extension(pool));
|
.layer(Extension(Arc::new(config)))
|
||||||
|
.layer(Extension(pool))
|
||||||
|
.layer(Extension(Arc::new(request_sender)));
|
||||||
|
|
||||||
axum::Server::from_tcp(tcp_listener)?
|
axum::Server::from_tcp(tcp_listener)?
|
||||||
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
.serve(app.into_make_service_with_connect_info::<SocketAddr>())
|
||||||
|
|
|
||||||
|
|
@ -7,60 +7,90 @@ use axum::{
|
||||||
Extension, TypedHeader,
|
Extension, TypedHeader,
|
||||||
};
|
};
|
||||||
use sqlx::mysql::MySqlPool;
|
use sqlx::mysql::MySqlPool;
|
||||||
use std::net::SocketAddr;
|
use std::{borrow::Cow, net::SocketAddr, sync::Arc};
|
||||||
use tokio::sync::oneshot;
|
|
||||||
|
|
||||||
use aquatic_http_protocol::{
|
use aquatic_http_protocol::{
|
||||||
request::AnnounceRequest,
|
request::AnnounceRequest,
|
||||||
response::{AnnounceResponse, FailureResponse, Response},
|
response::{FailureResponse, Response},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::workers::common::ChannelAnnounceRequest;
|
use crate::{
|
||||||
|
common::{ChannelRequestSender, RequestWorkerIndex},
|
||||||
|
config::Config,
|
||||||
|
};
|
||||||
|
|
||||||
use super::db;
|
use super::db;
|
||||||
|
|
||||||
pub async fn announce(
|
pub async fn announce(
|
||||||
|
Extension(config): Extension<Arc<Config>>,
|
||||||
Extension(pool): Extension<MySqlPool>,
|
Extension(pool): Extension<MySqlPool>,
|
||||||
|
Extension(request_sender): Extension<Arc<ChannelRequestSender>>,
|
||||||
ConnectInfo(peer_addr): ConnectInfo<SocketAddr>,
|
ConnectInfo(peer_addr): ConnectInfo<SocketAddr>,
|
||||||
opt_user_agent: Option<TypedHeader<UserAgent>>,
|
opt_user_agent: Option<TypedHeader<UserAgent>>,
|
||||||
Path(user_token): Path<String>,
|
Path(user_token): Path<String>,
|
||||||
RawQuery(query): RawQuery,
|
RawQuery(query): RawQuery,
|
||||||
) -> Result<(StatusCode, impl IntoResponse), (StatusCode, impl IntoResponse)> {
|
) -> Result<axum::response::Response, axum::response::Response> {
|
||||||
let request = AnnounceRequest::from_query_string(&query.unwrap_or_else(|| "".into()))
|
let query = query.ok_or_else(|| create_failure_response("Empty query string"))?;
|
||||||
.map_err(|err| build_response(Response::Failure(FailureResponse::new("Internal error"))))?;
|
|
||||||
|
|
||||||
|
let request = AnnounceRequest::from_query_string(&query)
|
||||||
|
.map_err(|_| create_failure_response("Malformed request"))?;
|
||||||
|
|
||||||
|
let request_worker_index = RequestWorkerIndex::from_info_hash(&config, request.info_hash);
|
||||||
let opt_user_agent = opt_user_agent.map(|header| header.as_str().to_owned());
|
let opt_user_agent = opt_user_agent.map(|header| header.as_str().to_owned());
|
||||||
|
|
||||||
let validated_request =
|
let validated_request =
|
||||||
db::validate_announce_request(&pool, peer_addr, opt_user_agent, user_token, request)
|
db::validate_announce_request(&pool, peer_addr, opt_user_agent, user_token, request)
|
||||||
.await
|
.await
|
||||||
.map_err(|r| build_response(Response::Failure(r)))?;
|
.map_err(|r| create_response(Response::Failure(r)))?;
|
||||||
|
|
||||||
let (response_sender, response_receiver) = oneshot::channel();
|
|
||||||
|
|
||||||
let canonical_socket_addr = CanonicalSocketAddr::new(peer_addr);
|
let canonical_socket_addr = CanonicalSocketAddr::new(peer_addr);
|
||||||
|
|
||||||
let channel_request = ChannelAnnounceRequest {
|
let response_receiver = request_sender
|
||||||
request: validated_request,
|
.send_to(
|
||||||
source_addr: canonical_socket_addr,
|
request_worker_index,
|
||||||
response_sender,
|
validated_request,
|
||||||
};
|
canonical_socket_addr,
|
||||||
|
)
|
||||||
// TODO: send request to request worker
|
.await
|
||||||
|
.map_err(|err| internal_error(format!("Sending request over channel failed: {:#}", err)))?;
|
||||||
|
|
||||||
let response = response_receiver.await.map_err(|err| {
|
let response = response_receiver.await.map_err(|err| {
|
||||||
::log::error!("channel response sender closed: {}", err);
|
internal_error(format!("Receiving response over channel failed: {:#}", err))
|
||||||
|
|
||||||
build_response(Response::Failure(FailureResponse::new("Internal error")))
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(build_response(Response::Announce(response)))
|
Ok(create_response(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_response(response: Response) -> (StatusCode, impl IntoResponse) {
|
fn create_response(response: Response) -> axum::response::Response {
|
||||||
let mut response_bytes = Vec::with_capacity(512);
|
let mut response_bytes = Vec::with_capacity(64);
|
||||||
|
|
||||||
response.write(&mut response_bytes);
|
response.write(&mut response_bytes).unwrap();
|
||||||
|
|
||||||
(StatusCode::OK, response_bytes)
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
[("Content-type", "text/plain; charset=utf-8")],
|
||||||
|
response_bytes,
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_failure_response<R: Into<Cow<'static, str>>>(reason: R) -> axum::response::Response {
|
||||||
|
let mut response_bytes = Vec::with_capacity(32);
|
||||||
|
|
||||||
|
FailureResponse::new(reason)
|
||||||
|
.write(&mut response_bytes)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
StatusCode::OK,
|
||||||
|
[("Content-type", "text/plain; charset=utf-8")],
|
||||||
|
response_bytes,
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_error(error: String) -> axum::response::Response {
|
||||||
|
::log::error!("{}", error);
|
||||||
|
|
||||||
|
create_failure_response("Internal error")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue