diff --git a/Cargo.toml b/Cargo.toml index b6e2c24e..afca46b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,12 @@ git = "https://github.com/seanmonstar/mime.rs" [dependencies.unsafe-any] git = "https://github.com/reem/rust-unsafe-any" +[dependencies.intertwine] +git = "https://github.com/reem/rust-intertwine" + +[dependencies.move-acceptor] +git = "https://github.com/reem/rust-move-acceptor" + [dev-dependencies.curl] git = "https://github.com/carllerche/curl-rust" diff --git a/src/lib.rs b/src/lib.rs index 6daec19a..c74b3c61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ extern crate url; #[phase(plugin,link)] extern crate log; #[cfg(test)] extern crate test; extern crate "unsafe-any" as uany; +extern crate "move-acceptor" as macceptor; +extern crate intertwine; pub use std::io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr, Port}; pub use mimewrapper::mime; diff --git a/src/server/mod.rs b/src/server/mod.rs index 5afbbf95..b061a12d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,11 +1,16 @@ //! HTTP Server -use std::io::{Acceptor, Listener, IoResult, EndOfFile, IncomingConnections}; +use std::io::{Listener, IoResult, EndOfFile}; use std::io::net::ip::{IpAddr, Port, SocketAddr}; +use intertwine::{Intertwine, Intertwined}; +use macceptor::MoveAcceptor; + pub use self::request::Request; pub use self::response::{Response, Fresh, Streaming}; -use net::{NetworkListener, NetworkAcceptor, NetworkStream, HttpAcceptor, HttpListener}; +use net::{NetworkListener, NetworkAcceptor, NetworkStream, HttpAcceptor, HttpListener, HttpStream}; + +use {HttpResult}; pub mod request; pub mod response; @@ -15,55 +20,71 @@ pub mod response; /// Once listening, it will create a `Request`/`Response` pair for each /// incoming connection, and hand them to the provided handler. pub struct Server { - ip: IpAddr, - port: Port + pairs: Vec<(IpAddr, Port)> } +macro_rules! try_option( + ($e:expr) => {{ + match $e { + Some(v) => v, + None => return None + } + }} +) + impl Server { /// Creates a new server that will handle `HttpStream`s. pub fn http(ip: IpAddr, port: Port) -> Server { - Server { - ip: ip, - port: port - } + Server { pairs: vec![(ip, port)] } + } + + /// Creates a server that can listen to many (ip, port) pairs. + pub fn many(pairs: Vec<(IpAddr, Port)>) -> Server { + Server { pairs: pairs } } } impl, S: NetworkStream, A: NetworkAcceptor> Server { - /// Creates a server that can listen for and handle `NetworkStreams`. - pub fn new(ip: IpAddr, port: Port) -> Server { - Server { - ip: ip, - port: port - } - } - /// Binds to a socket, and starts handling connections. - pub fn listen>(self, handler: H) -> IoResult> { - let mut listener: L = try!(NetworkListener::bind(self.ip.to_string().as_slice(), self.port)); - let socket = try!(listener.socket_name()); - let acceptor = try!(listener.listen()); - let mut worker = acceptor.clone(); + pub fn listen(self, handler: H) -> HttpResult> + where S: NetworkStream, + A: NetworkAcceptor, + H: Handler, + L: NetworkListener { + let mut acceptors = Vec::new(); + let mut sockets = Vec::new(); + for (ip, port) in self.pairs.move_iter() { + let mut listener: L = try_io!(NetworkListener::::bind(ip.to_string().as_slice(), port)); + + sockets.push(try_io!(listener.socket_name())); + + let acceptor = try_io!(listener.listen()); + acceptors.push(acceptor.clone()); + } + + let connections = acceptors.clone() + .move_iter() + .map(|acceptor| acceptor.move_incoming()) + .intertwine(); spawn(proc() { - handler.handle(Incoming { from: worker.incoming() }); + handler.handle(Incoming { from: connections }); }); Ok(Listening { - acceptor: acceptor, - socket_addr: socket, + acceptors: acceptors, + sockets: sockets, }) } - } /// An iterator over incoming connections, represented as pairs of /// hyper Requests and Responses. -pub struct Incoming<'a, A: 'a = HttpAcceptor> { - from: IncomingConnections<'a, A> +pub struct Incoming { + from: Intertwined> } -impl<'a, S: NetworkStream, A: NetworkAcceptor> Iterator<(Request, Response)> for Incoming<'a, A> { +impl Iterator<(Request, Response)> for Incoming { fn next(&mut self) -> Option<(Request, Response)> { for conn in self.from { match conn { @@ -93,17 +114,23 @@ impl<'a, S: NetworkStream, A: NetworkAcceptor> Iterator<(Request, Response { - acceptor: A, - /// The socket address that the server is bound to. - pub socket_addr: SocketAddr, +pub struct Listening { + acceptors: Vec, + /// The socket addresses that the server is bound to. + pub sockets: Vec, } impl, S: NetworkStream> Listening { - /// Stop the server from listening to it's socket address. - pub fn close(mut self) -> IoResult<()> { + /// Stop the server from listening to all of its socket addresses. + /// + /// If closing any of the servers acceptors fails, this function returns Err + /// and does not close the rest of the acceptors. + pub fn close(&mut self) -> HttpResult<()> { debug!("closing server"); - self.acceptor.close() + for acceptor in self.acceptors.mut_iter() { + try_io!(acceptor.close()); + } + Ok(()) } } @@ -112,11 +139,11 @@ pub trait Handler, S: NetworkStream>: 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(self, Incoming); + fn handle(self, Incoming); } -impl, S: NetworkStream> Handler for fn(Incoming) { - fn handle(self, incoming: Incoming) { +impl, S: NetworkStream> Handler for fn(Incoming) { + fn handle(self, incoming: Incoming) { (self)(incoming) } }