Search code examples
rustiron

Using iron framework, how to exit the listening loop?


I have a simple rest sort of skin around a large text file that I need to query interactively. It can involve a heavy compute so I used rust. I have put a simple restful skin with Iron. Note that I haven't done much with rust. This is just running on localhost.

pub fn query<'a>(parsed: &'a Parsed, context:&Context) -> {
  // parsed.keys is a hash containing config informtion
  // context is what I query
  let local_port = format!("{}:{}", "localhost", parsed.keys[AS_SERVER]);
  fn test_connect<'a>(r: &'a mut Request, c:&'a Context) -> IronResult<Response> {
    let url = r.url.to_string();
    let result = // some logic
    Ok(Response::with((status::Ok,  format!("requested=\"{}:{}\"\n", url, result))))
  } 
  let mut router = Router::new();
  router.get("*", move |r: &mut Request| test_connect(r, &context));
  let connection_status = Iron::new(router).http(&local_port[..]);
  connection_status.ok().expect("could not connect");
} 

Now my question is how do I get control to return out of the listening loop in

Iron::new(router).http(&local_port[..]);

I just want to say curl http://localhost/done

and have the listen function exit and so some logging and and move on. Is this possible to do this? I have tried panic-ing, but even that won't exit the listening loop?

I tried returning something like Err(IronError::new(StringError(url),status::ServiceUnavailable))

That status::ServiceUnavailable is just something random - it needed something: I know it's not semantically correct, not sure what to use. But the error doesn't have any handler on it, so it goes away silently.

I suppose I will set something up with aftermiddleware, but I don't know what?


Solution

  • One way of doing it is to set up messages between your handler and the thread that started the server. When the message is sent, the main thread can call Listening::close

    In this example, I used a Condvar. I'd expect you could also use a channel, but wasn't able to quickly copy and paste the example channel and get it working... ^_^

    extern crate iron;
    
    use iron::prelude::*;
    use iron::status;
    
    use std::sync::{Arc, Mutex, Condvar};
    
    fn main() {
        // Copied from docs
        let pair = Arc::new((Mutex::new(false), Condvar::new()));
        let pair2 = pair.clone();
    
        let handler = move |_: &mut Request| {
            // Copied from docs
            let &(ref lock, ref cvar) = &*pair2;
            let mut should_exit = lock.lock().unwrap();
            *should_exit = true;
            cvar.notify_one();
    
            Ok(Response::with((status::Ok, "Hello World!")))
        };
    
        // Hold onto the listener
        let mut serv = Iron::new(handler).http("localhost:3210").unwrap();
    
        println!("Now listening...");
    
        // Copied from docs
        let &(ref lock, ref cvar) = &*pair;
        let mut should_exit = lock.lock().unwrap();
        while !*should_exit {
            should_exit = cvar.wait(should_exit).unwrap();
        }
    
        serv.close().unwrap();
    
        println!("Exiting...");
    }