From c760ed063e1fcc69308b0708501e8fd2d74e6444 Mon Sep 17 00:00:00 2001 From: Jonathan Reem Date: Sun, 7 Sep 2014 09:15:35 +0200 Subject: [PATCH] Change the Handler trait to receive an Iterator of (Request, Response) pairs. This allows downstream users to have total control of their concurrency strategy, while also exposing a very nice, streaming interface for frameworks to build on. This also resolves issues surrounding the use of IoResult as the return type of Handler::handle, because handlers now have complete control over how to handle internal failure. Fixes #3 Fixes #4 --- src/server/mod.rs | 77 ++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index bc9f0bb8..fee5f150 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,6 +1,6 @@ //! HTTP Server use std::io::net::tcp::{TcpListener, TcpAcceptor}; -use std::io::{Acceptor, Listener, IoResult, EndOfFile}; +use std::io::{Acceptor, Listener, IoResult, EndOfFile, IncomingConnections}; use std::io::net::ip::{IpAddr, Port, SocketAddr}; pub use self::request::Request; @@ -30,7 +30,7 @@ impl Server { } /// Binds to a socket, and starts handling connections. - pub fn listen(self, mut handler: H) -> IoResult { + pub fn listen(self, handler: H) -> IoResult { let mut listener = try!(TcpListener::bind(self.ip.to_string().as_slice(), self.port)); let socket = try!(listener.socket_name()); let acceptor = try!(listener.listen()); @@ -38,34 +38,7 @@ impl Server { spawn(proc() { let mut acceptor = worker; - for conn in acceptor.incoming() { - match conn { - Ok(stream) => { - debug!("Incoming stream"); - let clone = stream.clone(); - let req = match Request::new(stream) { - Ok(r) => r, - Err(err) => { - error!("creating Request: {}", err); - continue; - } - }; - let mut res = Response::new(clone); - res.version = req.version; - match handler.handle(req, res) { - Ok(..) => debug!("Stream handled"), - Err(e) => { - error!("Error from handler: {}", e) - //TODO try to send a status code - } - } - }, - Err(ref e) if e.kind == EndOfFile => break, // server closed - Err(e) => { - error!("Connection failed: {}", e); - } - } - } + handler.handle(Incoming { from: acceptor.incoming() }); }); Ok(Listening { @@ -76,6 +49,41 @@ impl Server { } +/// An iterator over incoming connections, represented as pairs of +/// hyper Requests and Responses. +pub struct Incoming<'a> { + from: IncomingConnections<'a, TcpAcceptor> +} + +impl<'a> Iterator<(Request, Response)> for Incoming<'a> { + fn next(&mut self) -> Option<(Request, Response)> { + for conn in self.from { + match conn { + Ok(stream) => { + debug!("Incoming stream"); + let clone = stream.clone(); + let req = match Request::new(stream) { + Ok(r) => r, + Err(err) => { + error!("creating Request: {}", err); + continue; + } + }; + let mut res = Response::new(clone); + res.version = req.version; + return Some((req, res)) + }, + Err(ref e) if e.kind == EndOfFile => return None, // server closed + Err(e) => { + error!("Connection failed: {}", e); + continue; + } + } + } + None + } +} + /// A listening server, which can later be closed. pub struct Listening { acceptor: TcpAcceptor, @@ -96,11 +104,12 @@ pub trait Handler: Send { /// Receives a `Request`/`Response` pair, and should perform some action on them. /// /// This could reading from the request, and writing to the response. - fn handle(&mut self, req: Request, res: Response) -> IoResult<()>; + fn handle(self, Incoming); } -impl Handler for fn(Request, Response) -> IoResult<()> { - fn handle(&mut self, req: Request, res: Response) -> IoResult<()> { - (*self)(req, res) +impl Handler for fn(Incoming) { + fn handle(self, incoming: Incoming) { + (self)(incoming) } } +