committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							a22ae26cec
						
					
				
				
					commit
					d67dbc6028
				
			
							
								
								
									
										19
									
								
								src/net.rs
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/net.rs
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| //! A collection of traits abstracting over Listeners and Streams. | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::net::{SocketAddr}; | ||||
| use std::option; | ||||
|  | ||||
| use rotor::mio::tcp::{TcpStream, TcpListener}; | ||||
| use rotor::mio::{Selector, Token, Evented, EventSet, PollOpt, TryAccept}; | ||||
| @@ -168,6 +169,15 @@ impl Evented for HttpListener { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl IntoIterator for HttpListener { | ||||
|     type Item = Self; | ||||
|     type IntoIter = option::IntoIter<Self>; | ||||
|  | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         Some(self).into_iter() | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Deprecated | ||||
| /// | ||||
| /// Use `SslClient` and `SslServer` instead. | ||||
| @@ -390,6 +400,15 @@ impl<S: SslServer> Evented for HttpsListener<S> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<S: SslServer> IntoIterator for HttpsListener<S> { | ||||
|     type Item = Self; | ||||
|     type IntoIter = option::IntoIter<Self>; | ||||
|  | ||||
|     fn into_iter(self) -> Self::IntoIter { | ||||
|         Some(self).into_iter() | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn _assert_transport() { | ||||
|     fn _assert<T: Transport>() {} | ||||
|     _assert::<HttpsStream<HttpStream>>(); | ||||
|   | ||||
| @@ -37,19 +37,26 @@ impl<A: Accept, H: HandlerFactory<A::Output>> fmt::Debug for ServerLoop<A, H> { | ||||
|  | ||||
| /// A Server that can accept incoming network requests. | ||||
| #[derive(Debug)] | ||||
| pub struct Server<T: Accept> { | ||||
|     listener: T, | ||||
| pub struct Server<A> { | ||||
|     lead_listener: A, | ||||
|     other_listeners: Vec<A>, | ||||
|     keep_alive: bool, | ||||
|     idle_timeout: Option<Duration>, | ||||
|     max_sockets: usize, | ||||
| } | ||||
|  | ||||
| impl<T> Server<T> where T: Accept, T::Output: Transport { | ||||
|     /// Creates a new server with the provided Listener. | ||||
|     #[inline] | ||||
|     pub fn new(listener: T) -> Server<T> { | ||||
| impl<A: Accept> Server<A> { | ||||
|     /// Creates a new Server from one or more Listeners. | ||||
|     /// | ||||
|     /// Panics if listeners is an empty iterator. | ||||
|     pub fn new<I: IntoIterator<Item = A>>(listeners: I) -> Server<A> { | ||||
|         let mut listeners = listeners.into_iter(); | ||||
|         let lead_listener = listeners.next().expect("Server::new requires at least 1 listener"); | ||||
|         let other_listeners = listeners.collect::<Vec<_>>(); | ||||
|  | ||||
|         Server { | ||||
|             listener: listener, | ||||
|             lead_listener: lead_listener, | ||||
|             other_listeners: other_listeners, | ||||
|             keep_alive: true, | ||||
|             idle_timeout: Some(Duration::from_secs(10)), | ||||
|             max_sockets: 4096, | ||||
| @@ -59,7 +66,7 @@ impl<T> Server<T> where T: Accept, T::Output: Transport { | ||||
|     /// Enables or disables HTTP keep-alive. | ||||
|     /// | ||||
|     /// Default is true. | ||||
|     pub fn keep_alive(mut self, val: bool) -> Server<T> { | ||||
|     pub fn keep_alive(mut self, val: bool) -> Server<A> { | ||||
|         self.keep_alive = val; | ||||
|         self | ||||
|     } | ||||
| @@ -67,7 +74,7 @@ impl<T> Server<T> where T: Accept, T::Output: Transport { | ||||
|     /// Sets how long an idle connection will be kept before closing. | ||||
|     /// | ||||
|     /// Default is 10 seconds. | ||||
|     pub fn idle_timeout(mut self, val: Option<Duration>) -> Server<T> { | ||||
|     pub fn idle_timeout(mut self, val: Option<Duration>) -> Server<A> { | ||||
|         self.idle_timeout = val; | ||||
|         self | ||||
|     } | ||||
| @@ -75,7 +82,7 @@ impl<T> Server<T> where T: Accept, T::Output: Transport { | ||||
|     /// Sets the maximum open sockets for this Server. | ||||
|     /// | ||||
|     /// Default is 4096, but most servers can handle much more than this. | ||||
|     pub fn max_sockets(mut self, val: usize) -> Server<T> { | ||||
|     pub fn max_sockets(mut self, val: usize) -> Server<A> { | ||||
|         self.max_sockets = val; | ||||
|         self | ||||
|     } | ||||
| @@ -105,13 +112,11 @@ impl<S: SslServer> Server<HttpsListener<S>> { | ||||
| } | ||||
|  | ||||
|  | ||||
| impl<A: Accept> Server<A> where A::Output: Transport  { | ||||
| impl<A: Accept> Server<A> { | ||||
|     /// Binds to a socket and starts handling connections. | ||||
|     pub fn handle<H>(self, factory: H) -> ::Result<(Listening, ServerLoop<A, H>)> | ||||
|     where H: HandlerFactory<A::Output> { | ||||
|         let addr = try!(self.listener.local_addr()); | ||||
|         let shutdown = Arc::new(AtomicBool::new(false)); | ||||
|         let shutdown_rx = shutdown.clone(); | ||||
|          | ||||
|         let mut config = rotor::Config::new(); | ||||
|         config.slab_capacity(self.max_sockets); | ||||
| @@ -119,19 +124,36 @@ impl<A: Accept> Server<A> where A::Output: Transport  { | ||||
|         let keep_alive = self.keep_alive; | ||||
|         let idle_timeout = self.idle_timeout; | ||||
|         let mut loop_ = rotor::Loop::new(&config).unwrap(); | ||||
|  | ||||
|         let mut addrs = Vec::with_capacity(1 + self.other_listeners.len()); | ||||
|  | ||||
|         // Add the lead listener. This one handles shutdown messages. | ||||
|         let mut notifier = None; | ||||
|         { | ||||
|             let notifier = &mut notifier; | ||||
|             let listener = self.lead_listener; | ||||
|             addrs.push(try!(listener.local_addr())); | ||||
|             let shutdown_rx = shutdown.clone(); | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 *notifier = Some(scope.notifier()); | ||||
|                 rotor_try!(scope.register(&self.listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener::<A, H>(self.listener, shutdown_rx)) | ||||
|                 rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx)) | ||||
|             }).unwrap(); | ||||
|         } | ||||
|         let notifier = notifier.expect("loop.add_machine failed"); | ||||
|  | ||||
|         // Add the other listeners. | ||||
|         for listener in self.other_listeners { | ||||
|             addrs.push(try!(listener.local_addr())); | ||||
|             let shutdown_rx = shutdown.clone(); | ||||
|             loop_.add_machine_with(move |scope| { | ||||
|                 rotor_try!(scope.register(&listener, EventSet::readable(), PollOpt::level())); | ||||
|                 rotor::Response::ok(ServerFsm::Listener(listener, shutdown_rx)) | ||||
|             }).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let listening = Listening { | ||||
|             addr: addr, | ||||
|             addrs: addrs, | ||||
|             shutdown: (shutdown, notifier), | ||||
|         }; | ||||
|         let server = ServerLoop { | ||||
| @@ -299,14 +321,14 @@ where A: Accept, | ||||
|  | ||||
| /// A handle of the running server. | ||||
| pub struct Listening { | ||||
|     addr: SocketAddr, | ||||
|     addrs: Vec<SocketAddr>, | ||||
|     shutdown: (Arc<AtomicBool>, rotor::Notifier), | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Listening") | ||||
|             .field("addr", &self.addr) | ||||
|             .field("addrs", &self.addrs) | ||||
|             .field("closed", &self.shutdown.0.load(Ordering::Relaxed)) | ||||
|             .finish() | ||||
|     } | ||||
| @@ -314,14 +336,20 @@ impl fmt::Debug for Listening { | ||||
|  | ||||
| impl fmt::Display for Listening { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt::Display::fmt(&self.addr, f) | ||||
|         for (i, addr) in self.addrs().iter().enumerate() { | ||||
|             if i > 1 { | ||||
|                 try!(f.write_str(", ")); | ||||
|             } | ||||
|             try!(fmt::Display::fmt(addr, f)); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Listening { | ||||
|     /// The address this server is listening on. | ||||
|     pub fn addr(&self) -> &SocketAddr { | ||||
|         &self.addr | ||||
|     /// The addresses this server is listening on. | ||||
|     pub fn addrs(&self) -> &[SocketAddr] { | ||||
|         &self.addrs | ||||
|     } | ||||
|  | ||||
|     /// Stop the server from listening to its socket address. | ||||
| @@ -375,8 +403,3 @@ where F: FnMut(http::Control) -> H, H: Handler<T>, T: Transport { | ||||
|         self(ctrl) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ use std::sync::mpsc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use hyper::{Next, Encoder, Decoder}; | ||||
| use hyper::net::HttpStream; | ||||
| use hyper::net::{HttpListener, HttpStream}; | ||||
| use hyper::server::{Server, Handler, Request, Response}; | ||||
|  | ||||
| struct Serve { | ||||
| @@ -17,8 +17,14 @@ struct Serve { | ||||
| } | ||||
|  | ||||
| impl Serve { | ||||
|     fn addrs(&self) -> &[SocketAddr] { | ||||
|         self.listening.as_ref().unwrap().addrs() | ||||
|     } | ||||
|  | ||||
|     fn addr(&self) -> &SocketAddr { | ||||
|         self.listening.as_ref().unwrap().addr() | ||||
|         let addrs = self.addrs(); | ||||
|         assert!(addrs.len() == 1); | ||||
|         &addrs[0] | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| @@ -161,11 +167,22 @@ fn serve() -> Serve { | ||||
| } | ||||
|  | ||||
| fn serve_with_timeout(dur: Option<Duration>) -> Serve { | ||||
|     serve_n_with_timeout(1, dur) | ||||
| } | ||||
|  | ||||
| fn serve_n(n: u32) -> Serve { | ||||
|     serve_n_with_timeout(n, None) | ||||
| } | ||||
|  | ||||
| fn serve_n_with_timeout(n: u32, dur: Option<Duration>) -> Serve { | ||||
|     use std::thread; | ||||
|  | ||||
|     let (msg_tx, msg_rx) = mpsc::channel(); | ||||
|     let (reply_tx, reply_rx) = mpsc::channel(); | ||||
|     let (listening, server) = Server::http(&"127.0.0.1:0".parse().unwrap()).unwrap() | ||||
|  | ||||
|     let addr = "127.0.0.1:0".parse().unwrap(); | ||||
|     let listeners = (0..n).map(|_| HttpListener::bind(&addr).unwrap()); | ||||
|     let (listening, server) = Server::new(listeners) | ||||
|         .handle(move |_| { | ||||
|             let mut replies = Vec::new(); | ||||
|             while let Ok(reply) = reply_rx.try_recv() { | ||||
| @@ -180,7 +197,7 @@ fn serve_with_timeout(dur: Option<Duration>) -> Serve { | ||||
|         }).unwrap(); | ||||
|  | ||||
|  | ||||
|     let thread_name = format!("test-server-{}: {:?}", listening.addr(), dur); | ||||
|     let thread_name = format!("test-server-{}: {:?}", listening, dur); | ||||
|     thread::Builder::new().name(thread_name).spawn(move || { | ||||
|         server.run(); | ||||
|     }).unwrap(); | ||||
| @@ -439,3 +456,26 @@ fn server_keep_alive() { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn server_get_with_body_three_listeners() { | ||||
|     let server = serve_n(3); | ||||
|     let addrs = server.addrs(); | ||||
|     assert_eq!(addrs.len(), 3); | ||||
|  | ||||
|     for (i, addr) in addrs.iter().enumerate() { | ||||
|         let mut req = TcpStream::connect(addr).unwrap(); | ||||
|         write!(req, "\ | ||||
|             GET / HTTP/1.1\r\n\ | ||||
|             Host: example.domain\r\n\ | ||||
|             Content-Length: 17\r\n\ | ||||
|             \r\n\ | ||||
|             I'm sending to {}.\r\n\ | ||||
|         ", i).unwrap(); | ||||
|         req.read(&mut [0; 256]).unwrap(); | ||||
|  | ||||
|         // note: doesnt include trailing \r\n, cause Content-Length wasn't 19 | ||||
|         let comparison = format!("I'm sending to {}.", i).into_bytes(); | ||||
|         assert_eq!(server.body(), comparison); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user