Loop futures for client handling


#1

Hi there,

I have just started working with the futures crate by @alexcrichton and tried to implement a small echo server as an example.

Accepting clients is no problem, but in contrast to the example from the tutorial (https://github.com/alexcrichton/futures-rs/blob/master/TUTORIAL.md) I want to echo multiple messages from one client (a message begins with its length followed by the message body). I have managed to echo a single message, but no matter what I try I can’t manage to echo multiple message on the same connection.

Here the code of my handy_client:

fn handle_client(stream: futures_tls::TlsStream<TcpStream>){
        let mut lp = Loop::new().unwrap();

        let readlen = futures_io::read_exact(stream, vec![0;4]);
        let cont = readlen.and_then(| (steam, buff) | {
            let mut lenbuf:[u8; 4] = [0; 4];
            lenbuf.clone_from_slice(&(*buff));
            let len: u32 = unsafe{mem::transmute(lenbuf)};
            println!("new package\n\t len: {}", len);
            futures_io::read_exact(steam, vec![0; len as usize])
        });

        let write = cont.and_then( | (steam, buff) | {
            println!("\tbuff: {:?}", buff);
            let message = String::from_utf8(buff.clone()).unwrap();
            println!("\tmessage: {}", message);
            
            futures_io::write_all( steam, buff )
        });
        let _ = lp.run(write).unwrap();
}

Any idea how I can do that?
(I have already tried to loop over the whole content of handle_client and over let _ = lp.run(write).unwrap();)

Thanks in advance


#2

You may not want to create a new Loop here on this stack frame, but rather return a future representing the processed connection. To do that you could do it one of two ways:

First, you could use a stream and fold to execute the computation non-recursively:

stream::iter(iter::repeat(()).map(Ok)).fold(stream, |stream, ()| {
    // echo one message here
    handle_client(stream)
})

Next, you could also use recursion here to express a loop:

fn handle_client(stream) -> Future {
    // ...
    result.and_then(|stream| handle_client(stream))
}

The latter version is susceptible to stack overflow though if you’re under high load, so it’s recommended to use a non-recursive loop (perhaps through streams, like above)


#3

Seems to work.

Thanks for your help