WIP: 51 implement local data transfer #80
8 changed files with 195 additions and 9 deletions
|
|
@ -2,6 +2,7 @@ use std::{collections::HashMap, sync::Arc};
|
|||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::relay::room::Room;
|
||||
use crate::relay::transfer::Transfer;
|
||||
|
||||
/// A struct that holds all of the rooms that the server knows about.
|
||||
///
|
||||
|
|
@ -11,6 +12,7 @@ use crate::relay::room::Room;
|
|||
#[derive(Debug)]
|
||||
pub struct AppState {
|
||||
pub rooms: HashMap<String, Room>,
|
||||
pub transfers: Vec<Transfer>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
|
|
@ -44,15 +46,15 @@ impl AppState {
|
|||
Arc::new(RwLock::new(AppState {
|
||||
// Initialize the list of rooms to be empty.
|
||||
rooms: HashMap::new(),
|
||||
transfers: Vec::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::{Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
|
|
@ -60,4 +62,4 @@ mod tests {
|
|||
|
||||
assert!(Arc::ptr_eq(&app_state, &app_state.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -491,6 +491,5 @@ impl Client {
|
|||
// TODO: Add tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
}
|
||||
// use super::*;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ pub mod appstate;
|
|||
pub mod client;
|
||||
pub mod room;
|
||||
pub mod server;
|
||||
pub mod transfer;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@
|
|||
/// and running the application. If the server fails to bind to the specified
|
||||
/// host and port, it logs an error and exits.
|
||||
use axum::{
|
||||
extract::{ws::WebSocket, State, WebSocketUpgrade},
|
||||
extract::{ws::WebSocket, Json, Path, State, WebSocketUpgrade},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use serde_json::json;
|
||||
use std::{env, net::SocketAddr, sync::Arc};
|
||||
use tokio::{
|
||||
net::TcpListener,
|
||||
|
|
@ -34,6 +36,7 @@ use tracing::{debug, error, info, warn};
|
|||
|
||||
use crate::relay::appstate::AppState;
|
||||
use crate::relay::client::Client;
|
||||
use crate::relay::transfer::Transfer;
|
||||
|
||||
/// This function starts the WebSocket server.
|
||||
///
|
||||
|
|
@ -95,6 +98,9 @@ pub async fn start_ws(port: Option<&i32>, listen_addr: Option<&String>) {
|
|||
// Set up the application routes.
|
||||
let app = Router::new()
|
||||
.route("/ws", get(ws_handler))
|
||||
.route("/upload", post(upload_info))
|
||||
.route("/download/:name", get(download_info))
|
||||
.route("/download_success/:name", post(download_success))
|
||||
.with_state(server)
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
|
|
@ -272,3 +278,97 @@ async fn shutdown_signal() {
|
|||
_ = terminate => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upload_info(
|
||||
State(shared_state): State<Arc<RwLock<AppState>>>,
|
||||
// ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
Json(payload): Json<Transfer>,
|
||||
) -> impl IntoResponse {
|
||||
// debug!("Got upload request from {}", addr.ip().to_string());
|
||||
let mut data = shared_state.write().await;
|
||||
let t_request = Transfer {
|
||||
name: payload.name,
|
||||
ip: payload.ip,
|
||||
room_id: payload.room_id,
|
||||
};
|
||||
data.transfers.push(t_request.clone());
|
||||
|
||||
debug!("New TransferRequest created");
|
||||
debug!("Actual AppState is {:#?}", *data);
|
||||
|
||||
(StatusCode::CREATED, Json(t_request))
|
||||
}
|
||||
|
||||
pub async fn download_info(
|
||||
State(shared_state): State<Arc<RwLock<AppState>>>,
|
||||
Path(name): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
let data = shared_state.write().await;
|
||||
match data.transfers.iter().find(|request| request.name == name) {
|
||||
Some(request) => {
|
||||
debug!("Found transfer name.");
|
||||
(StatusCode::OK, Json(request.clone()))
|
||||
}
|
||||
None => {
|
||||
warn!("couldn't find transfer-name: {}", name);
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(Transfer {
|
||||
name: "".to_string(),
|
||||
ip: "".to_string(),
|
||||
room_id: "".to_string(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_success(
|
||||
State(shared_state): State<Arc<RwLock<AppState>>>,
|
||||
Path(name): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
let mut data = shared_state.write().await;
|
||||
if let Some(index) = data
|
||||
.transfers
|
||||
.iter()
|
||||
.position(|request| request.name == name)
|
||||
{
|
||||
debug!("Found Transfer by name '{name}'");
|
||||
data.transfers.remove(index);
|
||||
debug!("Transfer deleted");
|
||||
(
|
||||
StatusCode::OK,
|
||||
Json(json!({
|
||||
"message": "transfer deleted"
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
warn!("couldn't find transfer-name: {}", name);
|
||||
(
|
||||
StatusCode::NOT_FOUND,
|
||||
Json(json!({
|
||||
"message": "transfer not found"
|
||||
})),
|
||||
)
|
||||
}
|
||||
// match data.iter().find(|request| request.body.name == name) {
|
||||
// Some(request) => {
|
||||
// debug!("Found transfer name.");
|
||||
// return (
|
||||
// StatusCode::OK,
|
||||
// Json(json!({
|
||||
// "message" : "transfer deleted"
|
||||
// })),
|
||||
// );
|
||||
// }
|
||||
// None => {
|
||||
// warn!("couldn't find transfer-name: {}", name);
|
||||
// return (
|
||||
// StatusCode::NOT_FOUND,
|
||||
// Json(json!({
|
||||
// "message" : "transfer not found"
|
||||
// })),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
35
src/relay/transfer.rs
Normal file
35
src/relay/transfer.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct Transfer {
|
||||
pub name: String,
|
||||
pub ip: String,
|
||||
pub room_id: String,
|
||||
}
|
||||
|
||||
impl Transfer {
|
||||
pub fn new(name: String, ip: String, room_id: String) -> Self {
|
||||
Self { name, ip, room_id }
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let transfer = Transfer {
|
||||
name: "Test".to_string(),
|
||||
ip: "127.0.0.1".to_string(),
|
||||
room_id: "This_is_a_test_room_id".to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
Transfer::new(
|
||||
"Test".to_string(),
|
||||
"127.0.0.1".to_string(),
|
||||
"This_is_a_test_room_id".to_string()
|
||||
),
|
||||
transfer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
/// The `start` function takes ownership of the `WebSocketStream` and the file
|
||||
/// paths, so we pass it the `paths` vector by value.
|
||||
pub mod client;
|
||||
pub mod util;
|
||||
|
||||
use crate::sender::client as sender;
|
||||
use tokio_tungstenite::{
|
||||
|
|
|
|||
39
src/sender/util.rs
Normal file
39
src/sender/util.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use rand::{seq::SliceRandom, thread_rng};
|
||||
|
||||
fn generate_random_name() -> String {
|
||||
let mut rng = thread_rng();
|
||||
let adjective = adjectives().choose(&mut rng).unwrap();
|
||||
// let adjective = adjectives().sample(&mut rng).unwrap();
|
||||
let noun1 = nouns1().choose(&mut rng).unwrap();
|
||||
let noun2 = nouns2().choose(&mut rng).unwrap();
|
||||
|
||||
format!("{adjective}-{noun1}-{noun2}")
|
||||
}
|
||||
|
||||
fn adjectives() -> &'static [&'static str] {
|
||||
static ADJECTIVES: &[&str] = &["funny", "smart", "creative", "friendly", "great"];
|
||||
ADJECTIVES
|
||||
}
|
||||
|
||||
fn nouns1() -> &'static [&'static str] {
|
||||
static NOUNS1: &[&str] = &["dog", "cat", "flower", "tree", "house"];
|
||||
NOUNS1
|
||||
}
|
||||
|
||||
fn nouns2() -> &'static [&'static str] {
|
||||
static NOUNS2: &[&str] = &["cookie", "cake", "frosting"];
|
||||
NOUNS2
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_generate_random_name() {
|
||||
let name = generate_random_name();
|
||||
|
||||
assert!(name.contains('-'));
|
||||
assert!(name.split('-').count() == 3);
|
||||
assert!(name.len() > 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
use crate::relay::appstate::AppState;
|
||||
use crate::relay::server::download_info;
|
||||
use crate::relay::server::download_success;
|
||||
use crate::relay::server::upload_info;
|
||||
use crate::relay::server::ws_handler;
|
||||
use axum::{routing::get, Router};
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
use axum_client_ip::SecureClientIpSource;
|
||||
use shuttle_axum::ShuttleAxum;
|
||||
|
||||
|
|
@ -17,6 +23,9 @@ async fn axum() -> ShuttleAxum {
|
|||
// Set up the application routes.
|
||||
let app = Router::new()
|
||||
.route("/ws", get(ws_handler))
|
||||
.route("/upload", post(upload_info))
|
||||
.route("/download/:name", get(download_info))
|
||||
.route("/download_success/:name", post(download_success))
|
||||
.with_state(appstate)
|
||||
.layer(SecureClientIpSource::ConnectInfo.into_extension());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue