mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-27 02:04:39 +10:00
feat: manifest generation
This commit is contained in:
Generated
+19
-4
@@ -155,6 +155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
"bytes",
|
"bytes",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -200,6 +201,17 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-macros"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
@@ -544,8 +556,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "droplet-rs"
|
name = "droplet-rs"
|
||||||
version = "0.12.2"
|
version = "0.15.1"
|
||||||
source = "git+https://github.com/Drop-OSS/droplet-rs.git#05f7027b362fcc72b7cc621df8b5b0850b6cf082"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef23d187f7153bc3ce1c6a5440be1206e2aaf42b153825ddf2f8151f278539c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2108,6 +2121,7 @@ dependencies = [
|
|||||||
"file_open_limit",
|
"file_open_limit",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"num_cpus",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"protobuf-codegen",
|
"protobuf-codegen",
|
||||||
@@ -2120,6 +2134,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"url",
|
"url",
|
||||||
|
"uuid",
|
||||||
"waitmap",
|
"waitmap",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2233,9 +2248,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.19.0"
|
version = "1.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ name = "torrential"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.8.7"
|
axum = { version = "0.8.7", features = ["macros"] }
|
||||||
log = "0.4.28"
|
log = "0.4.28"
|
||||||
reqwest = { version = "0.12.24", default-features = false, features = [
|
reqwest = { version = "0.12.24", default-features = false, features = [
|
||||||
"json",
|
"json",
|
||||||
@@ -23,7 +23,7 @@ simple_logger = { version = "5.1.0", default-features = false, features = [
|
|||||||
"colors",
|
"colors",
|
||||||
] }
|
] }
|
||||||
tokio = { version = "*", features = ["rt-multi-thread", "sync"] }
|
tokio = { version = "*", features = ["rt-multi-thread", "sync"] }
|
||||||
droplet-rs = { git="https://github.com/Drop-OSS/droplet-rs.git" }
|
droplet-rs = "0.15.1"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.145"
|
||||||
@@ -38,6 +38,8 @@ file_open_limit = "0.0.5"
|
|||||||
pin-project-lite = "0.2.16"
|
pin-project-lite = "0.2.16"
|
||||||
protobuf = "3.7.2"
|
protobuf = "3.7.2"
|
||||||
waitmap = "1.1.0"
|
waitmap = "1.1.0"
|
||||||
|
uuid = { version = "1.20.0", features = ["v4"] }
|
||||||
|
num_cpus = "1.17.0"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
pedantic = { level = "warn", priority = -1 }
|
pedantic = { level = "warn", priority = -1 }
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
enum ResponseType {
|
enum TorrentialBoundType {
|
||||||
ERROR = 0;
|
ERROR = 0;
|
||||||
SERVER_GAMES_RESPONSE = 1;
|
SERVER_GAMES_RESPONSE = 1;
|
||||||
VERSION_RESPONSE = 2;
|
VERSION_RESPONSE = 2;
|
||||||
|
GENERATE_MANIFEST = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Response {
|
message TorrentialBound {
|
||||||
string message_id = 1;
|
string message_id = 1;
|
||||||
ResponseType type = 2;
|
TorrentialBoundType type = 2;
|
||||||
bytes data = 3;
|
bytes data = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum QueryType {
|
enum DropBoundType {
|
||||||
SERVER_GAMES_QUERY = 0;
|
SERVER_GAMES_QUERY = 0;
|
||||||
VERSION_QUERY = 1;
|
VERSION_QUERY = 1;
|
||||||
|
|
||||||
|
MANIFEST_PROGRESS = 2;
|
||||||
|
MANIFEST_LOG = 3;
|
||||||
|
MANIFEST_COMPLETE = 4;
|
||||||
|
MANIFEST_ERROR = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Query {
|
message DropBound {
|
||||||
string message_id = 1;
|
string message_id = 1;
|
||||||
QueryType type = 2;
|
DropBoundType type = 2;
|
||||||
bytes data = 3;
|
bytes data = 3;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
/// Certificates
|
||||||
|
message RootCertQuery {}
|
||||||
|
message RootCertResponse {
|
||||||
|
string cert = 1;
|
||||||
|
string priv = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClientCertQuery {
|
||||||
|
string client_id = 1;
|
||||||
|
string client_name = 2;
|
||||||
|
string root_cert = 3;
|
||||||
|
string root_priv = 4;
|
||||||
|
}
|
||||||
|
message ClientCertResponse {
|
||||||
|
string cert = 1;
|
||||||
|
string priv = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manifest generation
|
||||||
|
message GenerateManifest {
|
||||||
|
string version_dir = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ManifestProgress {
|
||||||
|
float progress = 1;
|
||||||
|
}
|
||||||
|
message ManifestLog {
|
||||||
|
string log_line = 1;
|
||||||
|
}
|
||||||
|
message ManifestComplete {
|
||||||
|
string manifest = 1;
|
||||||
|
}
|
||||||
|
message ManifestError {
|
||||||
|
string error = 1;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::proto::{self, version::version_response::Manifest};
|
use crate::proto::version::version_response::Manifest;
|
||||||
|
|
||||||
fn fixed_length<T, const N: usize>(v: Vec<T>) -> [T; N] {
|
fn fixed_length<T, const N: usize>(v: Vec<T>) -> [T; N] {
|
||||||
v.try_into()
|
v.try_into()
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{
|
sync::Arc,
|
||||||
Arc,
|
|
||||||
atomic::{AtomicUsize, Ordering},
|
|
||||||
},
|
|
||||||
task::Poll,
|
task::Poll,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,7 +21,7 @@ use tokio_util::io::ReaderStream;
|
|||||||
|
|
||||||
use crate::{server::download::fetch_instance_games, state::AppState};
|
use crate::{server::download::fetch_instance_games, state::AppState};
|
||||||
|
|
||||||
pub async fn healthcheck(State(state): State<Arc<AppState>>) -> StatusCode {
|
pub async fn healthcheck() -> StatusCode {
|
||||||
StatusCode::OK
|
StatusCode::OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +57,7 @@ const ZERO: [u8; 1024] = [0u8; _];
|
|||||||
impl AsyncRead for SpeedtestStream {
|
impl AsyncRead for SpeedtestStream {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
mut self: std::pin::Pin<&mut Self>,
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
_cx: &mut std::task::Context<'_>,
|
||||||
buf: &mut tokio::io::ReadBuf<'_>,
|
buf: &mut tokio::io::ReadBuf<'_>,
|
||||||
) -> Poll<std::io::Result<()>> {
|
) -> Poll<std::io::Result<()>> {
|
||||||
if self.remaining > 0 {
|
if self.remaining > 0 {
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod handlers;
|
||||||
|
pub mod serve;
|
||||||
|
pub mod download;
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{LazyCell, OnceCell},
|
|
||||||
io::Error,
|
io::Error,
|
||||||
rc::Rc,
|
|
||||||
sync::{Arc, LazyLock},
|
sync::{Arc, LazyLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -10,7 +8,7 @@ use axum::{
|
|||||||
body::Body,
|
body::Body,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::HeaderMap,
|
http::HeaderMap,
|
||||||
response::{AppendHeaders, IntoResponse},
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use dashmap::{DashMap, mapref::one::RefMut};
|
use dashmap::{DashMap, mapref::one::RefMut};
|
||||||
@@ -26,7 +24,8 @@ use tokio::sync::{Semaphore, SemaphorePermit};
|
|||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DownloadContext, GLOBAL_CONTEXT_SEMAPHORE, download::create_download_context, state::AppState,
|
DownloadContext, GLOBAL_CONTEXT_SEMAPHORE, downloads::download::create_download_context,
|
||||||
|
state::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
type Aes128Ctr64LE = ctr::Ctr64LE<aes::Aes128>;
|
type Aes128Ctr64LE = ctr::Ctr64LE<aes::Aes128>;
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::{info, warn};
|
||||||
|
use protobuf::Message;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::{spawn, sync::Semaphore};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
droplet,
|
||||||
|
proto::{
|
||||||
|
core::{DropBoundType, TorrentialBound},
|
||||||
|
droplet::{
|
||||||
|
GenerateManifest, ManifestComplete, ManifestError, ManifestLog, ManifestProgress,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server::DropServer,
|
||||||
|
};
|
||||||
|
|
||||||
|
static READER_SEMAPHORE: LazyLock<Semaphore> = LazyLock::new(|| {
|
||||||
|
let cores = num_cpus::get();
|
||||||
|
Semaphore::new(cores)
|
||||||
|
});
|
||||||
|
|
||||||
|
async fn generate_manifest_raw(
|
||||||
|
server: Arc<DropServer>,
|
||||||
|
message: TorrentialBound,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let manifest_message = GenerateManifest::parse_from_bytes(&message.data)?;
|
||||||
|
info!(
|
||||||
|
"seven zip install: {}",
|
||||||
|
*droplet_rs::versions::backends::SEVEN_ZIP_INSTALLED
|
||||||
|
);
|
||||||
|
|
||||||
|
let manifest = droplet_rs::manifest::generate_manifest_rusty(
|
||||||
|
&PathBuf::from(manifest_message.version_dir),
|
||||||
|
|progress| {
|
||||||
|
let mut progress_message = ManifestProgress::new();
|
||||||
|
progress_message.progress = progress;
|
||||||
|
|
||||||
|
let server = server.clone();
|
||||||
|
let message_id = message.message_id.clone();
|
||||||
|
spawn(async move {
|
||||||
|
let _ = server
|
||||||
|
.send_message(
|
||||||
|
DropBoundType::MANIFEST_PROGRESS,
|
||||||
|
progress_message,
|
||||||
|
Some(message_id),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|log_line| {
|
||||||
|
let mut progress_log = ManifestLog::new();
|
||||||
|
progress_log.log_line = log_line;
|
||||||
|
|
||||||
|
let server = server.clone();
|
||||||
|
let message_id = message.message_id.clone();
|
||||||
|
spawn(async move {
|
||||||
|
let _ = server
|
||||||
|
.send_message(DropBoundType::MANIFEST_LOG, progress_log, Some(message_id))
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(&READER_SEMAPHORE),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut manifest_complete = ManifestComplete::new();
|
||||||
|
manifest_complete.manifest = json!(manifest).to_string();
|
||||||
|
|
||||||
|
server
|
||||||
|
.send_message(
|
||||||
|
DropBoundType::MANIFEST_COMPLETE,
|
||||||
|
manifest_complete,
|
||||||
|
Some(message.message_id),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_manifest(server: Arc<DropServer>, message: TorrentialBound) {
|
||||||
|
let message_id = message.message_id.clone();
|
||||||
|
warn!("generating manifest...");
|
||||||
|
let result = generate_manifest_raw(server.clone(), message).await;
|
||||||
|
info!("manifest generation exited");
|
||||||
|
if let Err(err) = result {
|
||||||
|
warn!("manifest generation failed with err: {:?}", err);
|
||||||
|
let mut manifest_err = ManifestError::new();
|
||||||
|
manifest_err.error = err.to_string();
|
||||||
|
let _ = server
|
||||||
|
.send_message(
|
||||||
|
DropBoundType::MANIFEST_ERROR,
|
||||||
|
manifest_err,
|
||||||
|
Some(message_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
warn!("failed to send manifest err: {err:?}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod manifest;
|
||||||
|
pub mod cert;
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
pub mod download;
|
pub mod downloads;
|
||||||
pub mod serve;
|
|
||||||
pub mod handlers;
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
pub mod conversions;
|
pub mod conversions;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
pub mod droplet;
|
||||||
|
|
||||||
pub use download::DownloadContext;
|
pub use downloads::download::DownloadContext;
|
||||||
|
|
||||||
static GLOBAL_CONTEXT_SEMAPHORE: Semaphore = Semaphore::const_new(1);
|
static GLOBAL_CONTEXT_SEMAPHORE: Semaphore = Semaphore::const_new(1);
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ use dashmap::DashMap;
|
|||||||
use log::info;
|
use log::info;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use tokio::{runtime::Handle, spawn, time};
|
use tokio::{runtime::Handle, spawn, time};
|
||||||
use torrential::{handlers, serve, server::create_drop_server, state::AppState};
|
use torrential::{
|
||||||
|
downloads::{handlers, serve},
|
||||||
|
server::create_drop_server,
|
||||||
|
state::AppState,
|
||||||
|
};
|
||||||
|
|
||||||
const CONTEXT_TTL: u64 = 10 * 60;
|
const CONTEXT_TTL: u64 = 10 * 60;
|
||||||
|
|
||||||
@@ -28,7 +32,9 @@ async fn main() {
|
|||||||
let metrics = Handle::current().metrics();
|
let metrics = Handle::current().metrics();
|
||||||
info!("using {} threads", metrics.num_workers());
|
info!("using {} threads", metrics.num_workers());
|
||||||
|
|
||||||
let server = create_drop_server().await.expect("failed to connect to drop server");
|
let server = create_drop_server()
|
||||||
|
.await
|
||||||
|
.expect("failed to connect to drop server");
|
||||||
|
|
||||||
let shared_state = Arc::new(AppState {
|
let shared_state = Arc::new(AppState {
|
||||||
context_cache: DashMap::new(),
|
context_cache: DashMap::new(),
|
||||||
|
|||||||
@@ -1,15 +1,37 @@
|
|||||||
use crate::{proto::{manifest::server_games_response::SkeletonGame, version::VersionResponse}, state::{AppState}, util::ErrorOption};
|
use crate::{
|
||||||
|
proto::{
|
||||||
|
core::DropBoundType,
|
||||||
|
manifest::{ServerGamesQuery, ServerGamesResponse, server_games_response::SkeletonGame},
|
||||||
|
version::{VersionQuery, VersionResponse},
|
||||||
|
},
|
||||||
|
state::AppState,
|
||||||
|
util::ErrorOption,
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn fetch_version_data(
|
pub async fn fetch_version_data(
|
||||||
app_state: &AppState,
|
app_state: &AppState,
|
||||||
game_id: String,
|
_game_id: String,
|
||||||
version_id: String,
|
version_id: String,
|
||||||
) -> Result<VersionResponse, ErrorOption> {
|
) -> Result<VersionResponse, ErrorOption> {
|
||||||
unreachable!();
|
let mut query = VersionQuery::new();
|
||||||
|
query.version_id = version_id;
|
||||||
|
let message_id = app_state
|
||||||
|
.server
|
||||||
|
.send_message(DropBoundType::VERSION_QUERY, query, None)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response: VersionResponse = app_state.server.wait_for_message_id(&message_id).await?;
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_instance_games(
|
pub async fn fetch_instance_games(app_state: &AppState) -> Result<Vec<SkeletonGame>, ErrorOption> {
|
||||||
app_state: &AppState,
|
let message_id = app_state
|
||||||
) -> Result<Vec<SkeletonGame>, ErrorOption> {
|
.server
|
||||||
unreachable!()
|
.send_message(DropBoundType::SERVER_GAMES_QUERY, ServerGamesQuery::new(), None)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response: ServerGamesResponse = app_state.server.wait_for_message_id(&message_id).await?;
|
||||||
|
|
||||||
|
return Ok(response.games);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use protobuf::Message;
|
use log::{info, warn};
|
||||||
|
use protobuf::{EnumOrUnknown, Message};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncReadExt, AsyncWriteExt as _, BufReader},
|
io::{AsyncReadExt, AsyncWriteExt as _, BufReader},
|
||||||
net::{
|
net::{
|
||||||
@@ -9,57 +10,102 @@ use tokio::{
|
|||||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||||
},
|
},
|
||||||
spawn,
|
spawn,
|
||||||
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
use waitmap::WaitMap;
|
use waitmap::WaitMap;
|
||||||
|
|
||||||
use crate::proto::core::Response;
|
use crate::{
|
||||||
|
droplet::manifest::generate_manifest,
|
||||||
|
proto::core::{DropBound, DropBoundType, TorrentialBound, TorrentialBoundType},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod download;
|
pub mod download;
|
||||||
|
|
||||||
pub struct DropServer {
|
pub struct DropServer {
|
||||||
|
server: TcpListener,
|
||||||
write_stream: Mutex<OwnedWriteHalf>,
|
write_stream: Mutex<OwnedWriteHalf>,
|
||||||
waitmap: WaitMap<String, Response>,
|
waitmap: WaitMap<String, TorrentialBound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DropServer {
|
impl DropServer {
|
||||||
|
/**
|
||||||
|
Reads from the socket, and tries to parse it into a message,
|
||||||
|
and then updates the waitmap with the corresponding message ID
|
||||||
|
and content
|
||||||
|
*/
|
||||||
|
async fn recieve_loop(
|
||||||
|
myself: Arc<DropServer>,
|
||||||
|
buffered_reader: &mut BufReader<OwnedReadHalf>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let mut length_buffer: [u8; 8] = [0; 8];
|
||||||
|
buffered_reader.read_exact(&mut length_buffer).await?;
|
||||||
|
|
||||||
|
let length = usize::from_le_bytes(length_buffer);
|
||||||
|
let mut buffer = vec![0; length];
|
||||||
|
|
||||||
|
buffered_reader.read_exact(&mut buffer).await?;
|
||||||
|
|
||||||
|
let message = TorrentialBound::parse_from_bytes(&buffer)
|
||||||
|
.expect("response didn't deserialize correctly");
|
||||||
|
|
||||||
|
match message.type_.unwrap() {
|
||||||
|
TorrentialBoundType::GENERATE_MANIFEST => {
|
||||||
|
spawn(async move { generate_manifest(myself.clone(), message).await });
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
myself.waitmap.insert(message.message_id.clone(), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Long-lived subroutine that never returns, runs the recieve_loop and reconnects
|
||||||
|
as necessary
|
||||||
|
*/
|
||||||
async fn recieve_subroutine(myself: Arc<DropServer>, read_stream: OwnedReadHalf) -> ! {
|
async fn recieve_subroutine(myself: Arc<DropServer>, read_stream: OwnedReadHalf) -> ! {
|
||||||
let mut buffered_reader = BufReader::new(read_stream);
|
let mut buffered_reader = BufReader::new(read_stream);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut length_buffer: [u8; 8] = [0; 8];
|
if let Err(err) = Self::recieve_loop(myself.clone(), &mut buffered_reader).await {
|
||||||
buffered_reader
|
warn!("server disconnected with error: {:?}", err);
|
||||||
.read_exact(&mut length_buffer)
|
|
||||||
.await
|
|
||||||
.expect("failed to read from internal pipe");
|
|
||||||
|
|
||||||
let length = usize::from_le_bytes(length_buffer);
|
let (drop_stream, _) = myself
|
||||||
let mut buffer = Vec::with_capacity(length);
|
.server
|
||||||
|
.accept()
|
||||||
|
.await
|
||||||
|
.expect("failed to accept new listener");
|
||||||
|
let (read, mut write) = drop_stream.into_split();
|
||||||
|
|
||||||
buffered_reader
|
info!("reconnected to drop server");
|
||||||
.read_exact(&mut buffer)
|
|
||||||
.await
|
|
||||||
.expect("failed to read from internal pipe");
|
|
||||||
|
|
||||||
let message =
|
let mut lock = myself.write_stream.lock().await;
|
||||||
Response::parse_from_bytes(&buffer).expect("response didn't deserialize correctly");
|
mem::swap(&mut *lock, &mut write);
|
||||||
myself.waitmap.insert(message.message_id.clone(), message);
|
|
||||||
|
let mut new_reader = BufReader::new(read);
|
||||||
|
mem::swap(&mut buffered_reader, &mut new_reader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn wait_for_message_id<T>(&self, message_id: &str) -> Result<T, anyhow::Error>
|
/**
|
||||||
|
Uses the waitmap to wait for a response from a query
|
||||||
|
*/
|
||||||
|
pub async fn wait_for_message_id<T>(&self, message_id: &str) -> Result<T, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: protobuf::Message,
|
T: protobuf::Message,
|
||||||
{
|
{
|
||||||
let message = self
|
let message = self
|
||||||
.waitmap
|
.waitmap
|
||||||
.wait(message_id.clone())
|
.wait(message_id)
|
||||||
.await
|
.await
|
||||||
.ok_or(anyhow!("no response returned for value"))?;
|
.ok_or(anyhow!("no response returned for value"))?;
|
||||||
|
|
||||||
let message = message.value();
|
let message = message.value();
|
||||||
|
|
||||||
match message.type_.unwrap() {
|
match message.type_.unwrap() {
|
||||||
crate::proto::core::ResponseType::ERROR => {
|
crate::proto::core::TorrentialBoundType::ERROR => {
|
||||||
return Err(anyhow!(String::from_utf8(message.data.clone()).unwrap()));
|
return Err(anyhow!(String::from_utf8(message.data.clone()).unwrap()));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -69,26 +115,41 @@ impl DropServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_message<T>(&self, message: T) -> Result<(), anyhow::Error>
|
/**
|
||||||
|
Sends a message, returning the message ID
|
||||||
|
*/
|
||||||
|
pub async fn send_message<T>(
|
||||||
|
&self,
|
||||||
|
message_type: DropBoundType,
|
||||||
|
message: T,
|
||||||
|
message_id: Option<String>,
|
||||||
|
) -> Result<String, anyhow::Error>
|
||||||
where
|
where
|
||||||
T: protobuf::Message,
|
T: protobuf::Message,
|
||||||
{
|
{
|
||||||
|
let mut query = DropBound::new();
|
||||||
|
query.message_id = message_id.unwrap_or(uuid::Uuid::new_v4().to_string());
|
||||||
|
query.type_ = EnumOrUnknown::new(message_type);
|
||||||
|
query.data = Vec::new();
|
||||||
|
message.write_to_vec(&mut query.data)?;
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
message.write_to_vec(&mut buf)?;
|
query.write_to_vec(&mut buf)?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut mutex_lock = self
|
let mut mutex_lock = self.write_stream.lock().await;
|
||||||
.write_stream
|
|
||||||
.lock()
|
|
||||||
.expect("failed to lock send stream");
|
|
||||||
mutex_lock.write(&buf.len().to_le_bytes()).await?;
|
mutex_lock.write(&buf.len().to_le_bytes()).await?;
|
||||||
mutex_lock.write_all(&buf).await?;
|
mutex_lock.write_all(&buf).await?;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(query.message_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Spins up the TCP listener, and waits for the first client to connect
|
||||||
|
Also starts the recieve subroutine
|
||||||
|
*/
|
||||||
pub async fn create_drop_server() -> Result<Arc<DropServer>, anyhow::Error> {
|
pub async fn create_drop_server() -> Result<Arc<DropServer>, anyhow::Error> {
|
||||||
let server = TcpListener::bind("127.0.0.1:33148").await?;
|
let server = TcpListener::bind("127.0.0.1:33148").await?;
|
||||||
|
|
||||||
@@ -97,11 +158,14 @@ pub async fn create_drop_server() -> Result<Arc<DropServer>, anyhow::Error> {
|
|||||||
let (read, write) = drop_stream.into_split();
|
let (read, write) = drop_stream.into_split();
|
||||||
|
|
||||||
let client = Arc::new(DropServer {
|
let client = Arc::new(DropServer {
|
||||||
|
server,
|
||||||
write_stream: Mutex::new(write),
|
write_stream: Mutex::new(write),
|
||||||
waitmap: WaitMap::new(),
|
waitmap: WaitMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
spawn(DropServer::recieve_subroutine(client.clone(), read));
|
spawn(DropServer::recieve_subroutine(client.clone(), read));
|
||||||
|
|
||||||
|
info!("created client subroutine");
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user