4

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?

sgldiv
  • 623
  • 6
  • 16

2 Answers2

2

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...");
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks! Yes.. that was the way to do it. Side note though, cvar.notify_one(); makes it so the server response becomes empty - it shuts down without sending out the last response, but that's reasonable. – sgldiv Jul 23 '15 at 19:18
0

If all else fails, and no one can give you a better answer (in other words: this should be the option of last resort): std::process::exit should do the trick, one way or another. Keep in mind that this just immediately exits the process, so there's no cleanup done at all.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • ah, yes.. i could spawn and detach another instance of the server, and then exit. that way the detached copy can ultimately bind to sane port when it becomes available. it assumes there's only one thread querying it - otherwise it kills everyone else too (ideally it would be good to finish the requests that have been accepted), and everything loaded into memory is lost. but i will accept this if there's no other possibility. – sgldiv Jul 23 '15 at 06:08