summaryrefslogtreecommitdiff
path: root/rust/src/web.rs
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud.bailly@iohk.io>2024-10-04 17:17:10 +0200
committerArnaud Bailly <arnaud.bailly@iohk.io>2024-10-04 17:17:10 +0200
commitc7ecec3f7e64bfbc8dc0231d538599716e1ccf88 (patch)
treeb1c9ba512bb5d6df532417bafa66b824f5248773 /rust/src/web.rs
parent47533afd36990b486f1a5d66851f886510c04c20 (diff)
downloadlambda-nantes-c7ecec3f7e64bfbc8dc0231d538599716e1ccf88.tar.gz
Spawn thread to start sending requests to clients
Diffstat (limited to 'rust/src/web.rs')
-rw-r--r--rust/src/web.rs167
1 files changed, 106 insertions, 61 deletions
diff --git a/rust/src/web.rs b/rust/src/web.rs
index 257817d..45540e4 100644
--- a/rust/src/web.rs
+++ b/rust/src/web.rs
@@ -1,20 +1,25 @@
+use actix_web::{
+ get, http::header::ContentType, middleware::Logger, post, web, App, HttpResponse, HttpServer,
+ Responder,
+};
+use async_std::task;
+use futures::future::join_all;
+use futures::try_join;
+use serde::{Deserialize, Serialize};
use std::{
+ collections::HashMap,
env::args,
io::{stdin, stdout, IsTerminal},
- sync::Mutex,
+ sync::{Arc, Mutex},
};
-
-use actix_web::{
- get, http::header::ContentType, post, web, App, HttpResponse, HttpServer, Responder,
-};
-use serde::{Deserialize, Serialize};
+use uuid::Uuid;
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
struct Registration {
url: String,
}
-trait AppState {
+trait AppState: Send + Sync {
fn register(&mut self, registration: &Registration) -> RegistrationResult;
}
@@ -29,9 +34,51 @@ enum RegistrationFailure {
UrlAlreadyRegistered { url: String },
}
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+struct Client {
+ id: Uuid,
+ url: String,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+struct State {
+ clients: HashMap<String, Client>,
+}
+
+impl State {
+ fn new() -> Self {
+ Self {
+ clients: HashMap::new(),
+ }
+ }
+}
+
+impl AppState for State {
+ fn register(&mut self, registration: &Registration) -> RegistrationResult {
+ if self.clients.contains_key(&registration.url) {
+ RegistrationResult::UrlAlreadyRegistered {
+ url: registration.url.clone(),
+ }
+ } else {
+ let id = Uuid::new_v4();
+ self.clients.insert(
+ registration.url.clone(),
+ Client {
+ id,
+ url: registration.url.clone(),
+ },
+ );
+ RegistrationResult::RegistrationSuccess {
+ id: id.to_string(),
+ url: registration.url.clone(),
+ }
+ }
+ }
+}
+
#[post("/register")]
async fn register(
- app_state: web::Data<Mutex<dyn AppState>>,
+ app_state: web::Data<Arc<Mutex<dyn AppState>>>,
registration: web::Json<Registration>,
) -> impl Responder {
let result = app_state.lock().unwrap().register(&registration);
@@ -43,16 +90,43 @@ async fn register(
#[actix_web::main]
async fn main() -> std::io::Result<()> {
- HttpServer::new(|| App::new().service(register))
- .bind(("127.0.0.1", 8080))?
- .run()
- .await
+ let app_state = Arc::new(Mutex::new(State::new()));
+ let send_state: Arc<Mutex<State>> = app_state.clone();
+ let http_state: Arc<Mutex<dyn AppState>> = app_state.clone();
+ env_logger::init();
+ // let it run in the background
+ task::spawn(async move { send_tests(send_state).await });
+ HttpServer::new(move || {
+ App::new()
+ .wrap(Logger::default())
+ .app_data(web::Data::new(http_state.clone()))
+ .service(register)
+ })
+ .bind(("127.0.0.1", 8080))?
+ .run()
+ .await
+}
+
+async fn send_tests(clients: Arc<Mutex<State>>) -> ! {
+ loop {
+ task::sleep(std::time::Duration::from_secs(1)).await;
+ let clients = clients.lock().unwrap();
+ for client in clients.clients.values() {
+ send_test(client);
+ }
+ }
+}
+
+fn send_test(client: &Client) -> Result<(), String> {
+ println!("Sending test to {}", client.url);
+ Ok(())
}
#[cfg(test)]
mod app_tests {
use std::sync::Arc;
+ use actix_web::http::header::TryIntoHeaderValue;
use actix_web::{body, http::header::ContentType, middleware::Logger, test, App};
use super::*;
@@ -70,9 +144,9 @@ mod app_tests {
impl AppState for DummyAppState {
fn register(&mut self, registration: &Registration) -> RegistrationResult {
if self.id == "" {
- return RegistrationResult::UrlAlreadyRegistered {
+ RegistrationResult::UrlAlreadyRegistered {
url: registration.url.clone(),
- };
+ }
} else {
RegistrationResult::RegistrationSuccess {
id: self.id.clone(),
@@ -88,12 +162,12 @@ mod app_tests {
let dummy_state: Arc<Mutex<dyn AppState>> =
Arc::new(Mutex::new(DummyAppState::new(id.clone())));
// FIXME should only be called once, move to setup
- // env_logger::init();
+ env_logger::init();
let app = test::init_service(
App::new()
.wrap(Logger::default())
- .app_data(web::Data::from(dummy_state))
+ .app_data(web::Data::new(dummy_state))
.service(register),
)
.await;
@@ -117,25 +191,19 @@ mod app_tests {
}
#[actix_web::test]
- async fn post_registration_fails_given_url_is_already_registered() {
+ async fn post_registration_returns_400_when_register_fails() {
let dummy_state: Arc<Mutex<dyn AppState>> =
Arc::new(Mutex::new(DummyAppState::new("".to_string())));
let app = test::init_service(
App::new()
.wrap(Logger::default())
- .app_data(web::Data::from(dummy_state))
+ .app_data(web::Data::new(dummy_state))
.service(register),
)
.await;
let url = "http://192.168.1.1".to_string();
- let _ = test::TestRequest::post()
- .uri("/register")
- .set_json(Registration { url: url.clone() })
- .insert_header(ContentType::json())
- .to_request();
- // second request with the same URL
let req = test::TestRequest::post()
.uri("/register")
.set_json(Registration { url: url.clone() })
@@ -145,50 +213,27 @@ mod app_tests {
let resp = test::call_service(&app, req).await;
assert!(resp.status().is_client_error());
-
- let body = resp.into_body();
- let bytes = body::to_bytes(body).await;
assert_eq!(
- RegistrationResult::UrlAlreadyRegistered { url },
- serde_json::from_slice(&bytes.unwrap()).unwrap()
+ ContentType::json().try_into_value().unwrap(),
+ resp.headers().get("content-type").unwrap()
);
}
- #[actix_web::test]
- async fn post_registration_fails_given_url_is_already_registered() {
- let id = "0123456789abcdef0123456789abcdef".to_string();
- let id_generator: Arc<dyn IdGenerator> = Arc::new(ConstantIdGenerator::new(id.clone()));
+ #[test]
+ async fn app_does_not_register_same_url_twice() {
+ let mut app_state = State::new();
+ let registration = Registration {
+ url: "http://1.2.3.4".to_string(),
+ };
- let app = test::init_service(
- App::new()
- .wrap(Logger::default())
- .app_data(web::Data::from(id_generator))
- .service(register),
- )
- .await;
- let url = "http://192.168.1.1".to_string();
- let _ = test::TestRequest::post()
- .uri("/register")
- .set_json(Registration { url: url.clone() })
- .insert_header(ContentType::json())
- .to_request();
-
- // second request with the same URL
- let req = test::TestRequest::post()
- .uri("/register")
- .set_json(Registration { url: url.clone() })
- .insert_header(ContentType::json())
- .to_request();
-
- let resp = test::call_service(&app, req).await;
-
- assert!(resp.status().is_client_error());
+ app_state.register(&registration);
+ let result = app_state.register(&registration);
- let body = resp.into_body();
- let bytes = body::to_bytes(body).await;
assert_eq!(
- RegistrationResult::Failure(RegistrationFailure::UrlAlreadyRegistered { url }),
- serde_json::from_slice(&bytes.unwrap()).unwrap()
+ RegistrationResult::UrlAlreadyRegistered {
+ url: "http://1.2.3.4".to_string()
+ },
+ result
);
}
}