Eliminated a panic that can occur when using IrcServerFuture, and recommend using IrcReactor instead.

This commit is contained in:
Aaron Weiss 2018-01-27 19:50:48 +01:00
parent fd4e5706eb
commit 67cca339a7
No known key found for this signature in database
GPG key ID: 047D32DF25DC22EF
2 changed files with 33 additions and 20 deletions

View file

@ -1,10 +1,10 @@
//! A system for creating and managing IRC server connections. //! A system for creating and managing IRC server connections.
//! //!
//! This API provides the ability to create and manage multiple IRC servers that can run on the same //! This API provides the ability to create and manage multiple IRC servers that can run on the same
//! thread through the use of a shared event loop. It also replaces the old functionality of //! thread through the use of a shared event loop. It can also be used to encapsulate the dependency
//! `IrcServer::new_future` and better encapsulates dependencies on `tokio` and `futures`. Finally, //! on `tokio` and `futures` in the use of `IrcServer::new_future`. This means that knowledge of
//! it provides some escape hatches that let advanced users take advantage of these dependencies //! those libraries should be unnecessary for the average user. Nevertheless, this API also provides
//! regardless. //! some escape hatches that let advanced users take further advantage of these dependencies.
//! //!
//! # Example //! # Example
//! ```no_run //! ```no_run
@ -28,7 +28,7 @@ use futures::future;
use tokio_core::reactor::{Core, Handle}; use tokio_core::reactor::{Core, Handle};
use client::data::Config; use client::data::Config;
use client::server::{IrcServer, IrcServerFuture, Server}; use client::server::{IrcServer, IrcServerFuture, PackedIrcServer, Server};
use error; use error;
use proto::Message; use proto::Message;
@ -70,7 +70,7 @@ impl IrcReactor {
/// # } /// # }
/// ``` /// ```
pub fn prepare_server<'a>(&mut self, config: &'a Config) -> error::Result<IrcServerFuture<'a>> { pub fn prepare_server<'a>(&mut self, config: &'a Config) -> error::Result<IrcServerFuture<'a>> {
IrcServer::new_future(self.inner.handle(), config) IrcServer::new_future(self.inner_handle(), config)
} }
/// Runs an [IrcServerFuture](./server/struct.IrcServerFuture.html), such as one from /// Runs an [IrcServerFuture](./server/struct.IrcServerFuture.html), such as one from
@ -91,7 +91,10 @@ impl IrcReactor {
/// # } /// # }
/// ``` /// ```
pub fn connect_server(&mut self, future: IrcServerFuture) -> error::Result<IrcServer> { pub fn connect_server(&mut self, future: IrcServerFuture) -> error::Result<IrcServer> {
self.inner.run(future) self.inner.run(future).map(|PackedIrcServer(server, future)| {
self.register_future(future);
server
})
} }
/// Creates a new IRC server from the specified configuration, connecting immediately. This is /// Creates a new IRC server from the specified configuration, connecting immediately. This is

View file

@ -741,7 +741,9 @@ impl IrcServer {
/// Proper usage requires familiarity with `tokio` and `futures`. You can find more information /// Proper usage requires familiarity with `tokio` and `futures`. You can find more information
/// in the crate documentation for [tokio-core](http://docs.rs/tokio-core) or /// in the crate documentation for [tokio-core](http://docs.rs/tokio-core) or
/// [futures](http://docs.rs/futures). Additionally, you can find detailed tutorials on using /// [futures](http://docs.rs/futures). Additionally, you can find detailed tutorials on using
/// both libraries on the [tokio website](https://tokio.rs/docs/getting-started/tokio/). /// both libraries on the [tokio website](https://tokio.rs/docs/getting-started/tokio/). An easy
/// to use abstraction that does not require this knowledge is available via
/// [IrcReactors](../reactor/struct.IrcReactor.html).
/// ///
/// # Example /// # Example
/// ```no_run /// ```no_run
@ -749,6 +751,7 @@ impl IrcServer {
/// # extern crate tokio_core; /// # extern crate tokio_core;
/// # use std::default::Default; /// # use std::default::Default;
/// # use irc::client::prelude::*; /// # use irc::client::prelude::*;
/// # use irc::client::server::PackedIrcServer;
/// # use irc::error; /// # use irc::error;
/// # use tokio_core::reactor::Core; /// # use tokio_core::reactor::Core;
/// # fn main() { /// # fn main() {
@ -760,21 +763,21 @@ impl IrcServer {
/// let mut reactor = Core::new().unwrap(); /// let mut reactor = Core::new().unwrap();
/// let future = IrcServer::new_future(reactor.handle(), &config).unwrap(); /// let future = IrcServer::new_future(reactor.handle(), &config).unwrap();
/// // immediate connection errors (like no internet) will turn up here... /// // immediate connection errors (like no internet) will turn up here...
/// let server = reactor.run(future).unwrap(); /// let PackedIrcServer(server, future) = reactor.run(future).unwrap();
/// // runtime errors (like disconnections and so forth) will turn up here... /// // runtime errors (like disconnections and so forth) will turn up here...
/// reactor.run(server.stream().for_each(move |irc_msg| { /// reactor.run(server.stream().for_each(move |irc_msg| {
/// // processing messages works like usual /// // processing messages works like usual
/// process_msg(&server, irc_msg) /// process_msg(&server, irc_msg)
/// })).unwrap(); /// }).join(future)).unwrap();
/// # } /// # }
/// # fn process_msg(server: &IrcServer, message: Message) -> error::Result<()> { Ok(()) } /// # fn process_msg(server: &IrcServer, message: Message) -> error::Result<()> { Ok(()) }
/// ``` /// ```
pub(crate) fn new_future(handle: Handle, config: &Config) -> error::Result<IrcServerFuture> { pub fn new_future(handle: Handle, config: &Config) -> error::Result<IrcServerFuture> {
let (tx_outgoing, rx_outgoing) = mpsc::unbounded(); let (tx_outgoing, rx_outgoing) = mpsc::unbounded();
Ok(IrcServerFuture { Ok(IrcServerFuture {
conn: Connection::new(config, &handle)?, conn: Connection::new(config, &handle)?,
handle: handle, _handle: handle,
config: config, config: config,
tx_outgoing: Some(tx_outgoing), tx_outgoing: Some(tx_outgoing),
rx_outgoing: Some(rx_outgoing), rx_outgoing: Some(rx_outgoing),
@ -800,17 +803,18 @@ impl IrcServer {
/// Interaction with this future relies on the `futures` API, but is only expected for more advanced /// Interaction with this future relies on the `futures` API, but is only expected for more advanced
/// use cases. To learn more, you can view the documentation for the /// use cases. To learn more, you can view the documentation for the
/// [futures](https://docs.rs/futures/) crate, or the tutorials for /// [futures](https://docs.rs/futures/) crate, or the tutorials for
/// [tokio](https://tokio.rs/docs/getting-started/futures/). /// [tokio](https://tokio.rs/docs/getting-started/futures/). An easy to use abstraction that does
/// not require this knowledge is available via [IrcReactors](../reactor/struct.IrcReactor.html).
pub struct IrcServerFuture<'a> { pub struct IrcServerFuture<'a> {
conn: ConnectionFuture<'a>, conn: ConnectionFuture<'a>,
handle: Handle, _handle: Handle,
config: &'a Config, config: &'a Config,
tx_outgoing: Option<UnboundedSender<Message>>, tx_outgoing: Option<UnboundedSender<Message>>,
rx_outgoing: Option<UnboundedReceiver<Message>>, rx_outgoing: Option<UnboundedReceiver<Message>>,
} }
impl<'a> Future for IrcServerFuture<'a> { impl<'a> Future for IrcServerFuture<'a> {
type Item = IrcServer; type Item = PackedIrcServer;
type Error = error::Error; type Error = error::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
@ -822,19 +826,25 @@ impl<'a> Future for IrcServerFuture<'a> {
let outgoing_future = sink.send_all(self.rx_outgoing.take().unwrap().map_err(|()| { let outgoing_future = sink.send_all(self.rx_outgoing.take().unwrap().map_err(|()| {
let res: error::Error = error::ErrorKind::ChannelError.into(); let res: error::Error = error::ErrorKind::ChannelError.into();
res res
})).map(|_| ()).map_err(|e| panic!(e)); })).map(|_| ());
self.handle.spawn(outgoing_future); let server = IrcServer {
Ok(Async::Ready(IrcServer {
state: Arc::new(ServerState::new( state: Arc::new(ServerState::new(
stream, self.tx_outgoing.take().unwrap(), self.config.clone() stream, self.tx_outgoing.take().unwrap(), self.config.clone()
)), )),
view: view, view: view,
})) };
Ok(Async::Ready(PackedIrcServer(server, Box::new(outgoing_future))))
} }
} }
/// An `IrcServer` packaged with a future that drives its message sending. In order for the server
/// to actually work properly, this future _must_ be running.
///
/// This type should only be used by advanced users who are familiar with the implementation of this
/// crate. An easy to use abstraction that does not require this knowledge is available via
/// [IrcReactors](../reactor/struct.IrcReactor.html).
pub struct PackedIrcServer(pub IrcServer, pub Box<Future<Item = (), Error = error::Error>>);
#[cfg(test)] #[cfg(test)]
mod test { mod test {