0

I am trying to create a Actix server that I want to use as an interface for a global HashMap.

I have been able to create a route that returns the entire structure. However, now I am having problems updating the HashMap. I am able to submit and receive POST requests, however after attempting to use the publish route followed by the get_topics route, the resulting HashMap is empty {}.

// Publish to topic (creating new topic if none of the same name exists, else update the data)
async fn publish_topic(
    topics: web::Data<Mutex<Map<String, Value>>>,
    body: web::Bytes,
) -> Result<HttpResponse, Error> {
    let result = serde_json::from_str(std::str::from_utf8(&body).unwrap());
    let publish_req: Info = match result {
        Ok(v) => v,
        Err(e) => Info {
            topic: "_".to_string(),
            data: json!(e.to_string()),
        },
    };
    println!("[ SERVER ]: POST Req: {:?}", publish_req);
    topics
        .lock()
        .unwrap()
        .insert(publish_req.topic, publish_req.data);
    let map = topics.lock().unwrap().clone();
    println!("\n\n{:?}\n\n", topics.lock().unwrap());
    let body = serde_json::to_string_pretty(&map).unwrap();
    return Ok(HttpResponse::Ok().json(body));
}

#[actix_web::main]
pub async fn start_server() {
    std::env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();

    let (tx, _) = mpsc::channel();

    thread::spawn(move || {
        let sys = System::new("http-server");

        let srv = HttpServer::new(move || {
            let topics: web::Data<Mutex<Map<String, Value>>> =
                web::Data::new(Mutex::new(Map::new()));

            App::new()
                .app_data(topics.clone()) // add shared state
                // enable logger
                .wrap(middleware::Logger::default())
                // register simple handler
                .service(web::resource("/").route(web::get().to(get_topics)))
                .service(web::resource("/publish").route(web::post().to(publish_topic)))
        })
        .bind("127.0.0.1:8080")?
        .run();

        let _ = tx.send(srv);
        sys.run()
    });
}
bleuj
  • 151
  • 6

1 Answers1

1

Well, your problem stems from your approach. Lets look at the line below:

let srv = HttpServer::new(move || {
      let topics: web::Data<Mutex<Map<String, Value>>> =
          web::Data::new(Mutex::new(Map::new()));
          ...

Because of how actix-web uses an application factory to create a new http server, this is telling it to create a new map of topics in every thread. I am guessing that's not what you are trying to do yourself. To solve your problem, you will need to create on instance of topics and clone it for all threads started. This will work:

let topics: web::Data<Mutex<Map<String, Value>>> =
            web::Data::new(Mutex::new(Map::new()));

HttpServer::new(move || {
    App::new()
        .app_data(topics.clone())

This answer can also be a good reference.

Njuguna Mureithi
  • 3,506
  • 1
  • 21
  • 41