1

I want to respond with a different value to "the same request" depending on the state of server.

My expectation is that these responses will happen in a loop:

  • Someone sends a GET / request, it responds with "Hello, World!".
  • Then they send a GET / request, it responds with "Hello, Rust!".
  • Then they send a GET / request, it responds with "Hello, Rocket!".
  • Then they send a GET / request, it responds with "Hello, State!".

I don't want to use a variable that was initialized in main (in the Rocket route handler). I want to use a variable whose value can be changed in main (in the Rocket route handler).

This code compiles without error but the actual behavior is that it always responds "Hello, World!".

#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]

#[macro_use]
extern crate rocket;

#[macro_use]
extern crate lazy_static;

use std::sync::{Arc, Mutex};
use rocket::State;

use std::thread;
use std::time::Duration;

lazy_static! {
    static ref RESPONSE: Arc<Mutex<String>>
         = Arc::new(Mutex::new(String::from("Hello, World!")));
}

#[get("/")]
fn get_response(state: State<Arc<Mutex<String>>>) -> String {
    let response = state.lock().unwrap();
    let ret: String = String::from(response.as_str());
    ret
}

fn main() {
    let managed_response: Arc<Mutex<String>> = RESPONSE.clone();
    rocket::ignite()
        .manage(managed_response)
        .mount("/", routes![get_response])
        .launch();

    let mut server_state = 0;
    loop {
        // Pseudo transition of the state of server
        thread::sleep(Duration::from_secs(5));
        let mut response = RESPONSE.lock().unwrap();
        match server_state {
            0 => *response = String::from("Hello, Rust!"), // state 0
            1 => *response = String::from("Hello, Rocket!"), // state 1
            2 => *response = String::from("Hello, State!"), // state 2
            _ => panic!(),
        }
        server_state += 1;
        if server_state >= 3 {
            server_state = 0;
        }
    }
}
lenna_kun
  • 43
  • 5
  • Welcome to Stack Overflow! It looks like your question might be answered by the answers of [How can I pass a variable initialized in main to a Rocket route handler?](https://stackoverflow.com/q/55384204/155423). You may also be interested in [How do I create a global, mutable singleton?](https://stackoverflow.com/q/27791532/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Aug 21 '19 at 15:55
  • There's a lot of things that you don't *want* to do. You are going to have a hard time solving programming problems if you continue to discard solutions without reasonable technical reasons for why those solutions are not applicable. – Shepmaster Aug 21 '19 at 16:30
  • Do you mean about the solution that use global variable? – lenna_kun Aug 21 '19 at 16:36
  • Your question asks to change the response based on a sequence of requests. However, your code modifies the response based on a thread::sleep() call. In other words, it is likely that the first person who makes a request will not see the first message. Wouldn't it be better to modify the message to be sent in the request processing, rather than in a timed thread? – Gardener Aug 21 '19 at 17:23
  • Thanks your comment. Sorry, the word choice was inappropriate. I don't want to change the response based on a sequence of requests. I want to change it based on a state of server. – lenna_kun Aug 21 '19 at 23:49
  • I don't know rocket, so I may be wrong, but I suspect that `launch` blocks while the server is running and that your code never enters the loop… – Jmb Aug 22 '19 at 06:21
  • Sounds about right... – lenna_kun Aug 22 '19 at 07:10
  • umm, in reality, I want to solve this with Rocket::State. (not with lazy_state or the other way that use global variable) – lenna_kun Aug 22 '19 at 09:11
  • [The duplicates applied to your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=97ba0a338356b7c89f0ea073bd611e02) – Shepmaster Aug 26 '19 at 14:51

1 Answers1

1

Through trial and error, I solved it by myself. This is the code. If you have other better solutions, I would like you to teach me.

#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]

#[macro_use]
extern crate rocket;

#[macro_use]
extern crate lazy_static;

use std::sync::{Arc, Mutex};

use std::thread;
use std::time::Duration;

lazy_static! {
    static ref RESPONSE: Arc<Mutex<String>> = Arc::new(Mutex::new(String::from("Hello, World!")));
}

#[get("/")]
fn get_response() -> String {
    let response = RESPONSE.lock().unwrap();
    let ret: String = String::from(response.as_str());
    ret
}

fn main_code() {
    let mut server_state = 0;
    loop {
        thread::sleep(Duration::from_secs(5));
        let mut response = RESPONSE.lock().unwrap();
        match server_state {
            0 => *response = String::from("Hello, Rust!"),
            1 => *response = String::from("Hello, Rocket!"),
            2 => *response = String::from("Hello, State!"),
            _ => panic!(),
        }
        server_state += 1;
        if server_state >= 3 {
            server_state = 0;
        }
    }
}

fn main() {
    let handle = thread::spawn(|| {
        main_code();
    });
    rocket::ignite()
        .mount("/", routes![get_response])
        .launch();
}
lenna_kun
  • 43
  • 5