diff options
Diffstat (limited to 'src/itf_http.rs')
-rw-r--r-- | src/itf_http.rs | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/itf_http.rs b/src/itf_http.rs new file mode 100644 index 0000000..f3995cd --- /dev/null +++ b/src/itf_http.rs @@ -0,0 +1,118 @@ +use mio::{Ready,Token,Event}; +use mio::tcp::TcpStream; +use std::net::SocketAddr; +use std::io::ErrorKind; +use eventloop::{Machine,Context}; +use netbuf::Buf; + + +// TODO: timeouts +// TODO: Set a max size on the buffers and provide backpressure +pub struct ItfHttp { + sock: TcpStream, + addr: SocketAddr, + io: Token, + ioreg: Ready, + rbuf: Buf, + wbuf: Buf +} + + +impl ItfHttp { + pub fn new(sock: TcpStream, addr: SocketAddr) -> ItfHttp { + ItfHttp { + sock: sock, + addr: addr, + io: Token(0), + ioreg: Ready::readable(), + rbuf: Buf::new(), + wbuf: Buf::new() + } + } + + fn handle_read(&mut self, ctx: &mut Context) { + match self.rbuf.read_from(&mut self.sock) { + Err(err) => { + match err.kind() { + ErrorKind::WouldBlock | ErrorKind::Interrupted => { }, + _ => { + debug!("{}: Read error: {}", self.addr, err); + self.remove(ctx); + } + } + return; + }, + Ok(0) => { // This behaviour isn't documented, unsure if it's intended. + debug!("{}: Connection closed", self.addr); + self.remove(ctx); + return; + }, + _ => {} + } + // This is where we parse the stuff, generate a request object, throw the request object + // through some handlers to get a response and send it back. But for now let's just act + // like we wanted to be an echo server all along. + self.rbuf.write_to(&mut self.wbuf).unwrap(); + } + + fn handle_write(&mut self, ctx: &mut Context) { + if let Err(err) = self.wbuf.write_to(&mut self.sock) { + match err.kind() { + ErrorKind::WouldBlock | ErrorKind::Interrupted => { }, + _ => { + debug!("{}: Write error: {}", self.addr, err); + self.remove(ctx); + } + } + } + } + + fn set_ioreg(&mut self, ctx: &mut Context) { + // Optimization: If the write buffer was empty before but contains data now, we can already + // try to write it out at this point. This way we can avoid going through the event loop if + // the OS buffers have enough space. + // TODO: Measure how effective this is in practice; if this only tends to happen on the + // first write to a new socket then it might not be worth the effort. + if self.wbuf.len() > 0 && !self.ioreg.contains(Ready::writable()) { + self.handle_write(ctx); + if ctx.is_removed() { + return; + } + } + + let mut reg = Ready::readable(); + if self.wbuf.len() > 0 { + reg.insert(Ready::writable()); + } + if reg != self.ioreg { + self.ioreg = reg; + ctx.reregister(&self.sock, self.io, self.ioreg); + } + } + + fn remove(&mut self, ctx: &mut Context) { + ctx.deregister(&self.sock); + ctx.unassign(self.io); + ctx.remove(); + } +} + + +impl Machine for ItfHttp { + fn init(&mut self, ctx: &mut Context) { + self.io = ctx.assign(); + ctx.register(&self.sock, self.io, self.ioreg); + } + + fn handle(&mut self, ctx: &mut Context, ev: Event) { + if ev.kind().is_readable() { + self.handle_read(ctx); + } + if !ctx.is_removed() && ev.kind().is_writable() { + self.handle_write(ctx); + } + if !ctx.is_removed() { + self.set_ioreg(ctx); + } + } +} |