From b9bd2879620a877cfa514ff0a056c8e0f2fc97fc Mon Sep 17 00:00:00 2001 From: Todd Mortimer Date: Sun, 23 May 2021 15:28:07 -0400 Subject: [PATCH 1/5] Change "tls" feature to "native-tls" for clarity and obvious distinction with rustls-tls. --- Cargo.toml | 3 +-- src/client.rs | 6 +++--- src/client_builder.rs | 10 +++++----- src/error.rs | 24 ++++++++++++------------ src/extensions/idle.rs | 6 +++--- src/lib.rs | 2 +- src/types/deleted.rs | 2 +- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fbe93e39..c64a903f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,8 @@ keywords = ["email", "imap"] categories = ["email", "network-programming"] [features] -tls = ["native-tls"] rustls-tls = ["rustls-connector"] -default = ["tls"] +default = ["native-tls"] [dependencies] native-tls = { version = "0.2.2", optional = true } diff --git a/src/client.rs b/src/client.rs index 0bc5b296..75baf7fd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -284,7 +284,7 @@ impl Client { /// # use imap::Client; /// # use std::io; /// # use std::net::TcpStream; - /// # {} #[cfg(feature = "tls")] + /// # {} #[cfg(feature = "native-tls")] /// # fn main() { /// # let server = "imap.example.com"; /// # let username = ""; @@ -325,7 +325,7 @@ impl Client { /// transferred back to the caller. /// /// ```rust,no_run - /// # {} #[cfg(feature = "tls")] + /// # {} #[cfg(feature = "native-tls")] /// # fn main() { /// let client = imap::ClientBuilder::new("imap.example.org", 993) /// .native_tls().unwrap(); @@ -376,7 +376,7 @@ impl Client { /// } /// } /// - /// # {} #[cfg(feature = "tls")] + /// # {} #[cfg(feature = "native-tls")] /// fn main() { /// let auth = OAuth2 { /// user: String::from("me@example.com"), diff --git a/src/client_builder.rs b/src/client_builder.rs index 01cac2db..a037b729 100644 --- a/src/client_builder.rs +++ b/src/client_builder.rs @@ -2,7 +2,7 @@ use crate::{Client, Result}; use std::io::{Read, Write}; use std::net::TcpStream; -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] use native_tls::{TlsConnector, TlsStream}; #[cfg(feature = "rustls-tls")] use rustls_connector::{RustlsConnector, TlsStream as RustlsStream}; @@ -12,7 +12,7 @@ use rustls_connector::{RustlsConnector, TlsStream as RustlsStream}; /// Creating a [`Client`] using `native-tls` transport is straightforward: /// ```no_run /// # use imap::ClientBuilder; -/// # {} #[cfg(feature = "tls")] +/// # {} #[cfg(feature = "native-tls")] /// # fn main() -> Result<(), imap::Error> { /// let client = ClientBuilder::new("imap.example.com", 993).native_tls()?; /// # Ok(()) @@ -66,15 +66,15 @@ where } /// Use [`STARTTLS`](https://tools.ietf.org/html/rfc2595) for this connection. - #[cfg(any(feature = "tls", feature = "rustls-tls"))] + #[cfg(any(feature = "native-tls", feature = "rustls-tls"))] pub fn starttls(&mut self) -> &mut Self { self.starttls = true; self } /// Return a new [`Client`] using a `native-tls` transport. - #[cfg(feature = "tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] + #[cfg(feature = "native-tls")] + #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))] pub fn native_tls(&mut self) -> Result>> { self.connect(|domain, tcp| { let ssl_conn = TlsConnector::builder().build()?; diff --git a/src/error.rs b/src/error.rs index 16afbf5f..2dda68c2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,9 +10,9 @@ use std::str::Utf8Error; use base64::DecodeError; use bufstream::IntoInnerError as BufError; use imap_proto::{types::ResponseCode, Response}; -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] use native_tls::Error as TlsError; -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] use native_tls::HandshakeError as TlsHandshakeError; #[cfg(feature = "rustls-tls")] use rustls_connector::HandshakeError as RustlsHandshakeError; @@ -62,10 +62,10 @@ pub enum Error { #[cfg(feature = "rustls-tls")] RustlsHandshake(RustlsHandshakeError), /// An error from the `native_tls` library during the TLS handshake. - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] TlsHandshake(TlsHandshakeError), /// An error from the `native_tls` library while managing the socket. - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Tls(TlsError), /// A BAD response from the IMAP server. Bad(Bad), @@ -114,14 +114,14 @@ impl From> for Error { } } -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] impl From> for Error { fn from(err: TlsHandshakeError) -> Error { Error::TlsHandshake(err) } } -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] impl From for Error { fn from(err: TlsError) -> Error { Error::Tls(err) @@ -140,9 +140,9 @@ impl fmt::Display for Error { Error::Io(ref e) => fmt::Display::fmt(e, f), #[cfg(feature = "rustls-tls")] Error::RustlsHandshake(ref e) => fmt::Display::fmt(e, f), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::Tls(ref e) => fmt::Display::fmt(e, f), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::TlsHandshake(ref e) => fmt::Display::fmt(e, f), Error::Validate(ref e) => fmt::Display::fmt(e, f), Error::Parse(ref e) => fmt::Display::fmt(e, f), @@ -163,9 +163,9 @@ impl StdError for Error { Error::Io(ref e) => e.description(), #[cfg(feature = "rustls-tls")] Error::RustlsHandshake(ref e) => e.description(), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::Tls(ref e) => e.description(), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::TlsHandshake(ref e) => e.description(), Error::Parse(ref e) => e.description(), Error::Validate(ref e) => e.description(), @@ -183,9 +183,9 @@ impl StdError for Error { Error::Io(ref e) => Some(e), #[cfg(feature = "rustls-tls")] Error::RustlsHandshake(ref e) => Some(e), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::Tls(ref e) => Some(e), - #[cfg(feature = "tls")] + #[cfg(feature = "native-tls")] Error::TlsHandshake(ref e) => Some(e), Error::Parse(ParseError::DataNotUtf8(_, ref e)) => Some(e), _ => None, diff --git a/src/extensions/idle.rs b/src/extensions/idle.rs index b55d4f08..5c6f9787 100644 --- a/src/extensions/idle.rs +++ b/src/extensions/idle.rs @@ -5,7 +5,7 @@ use crate::client::Session; use crate::error::{Error, Result}; use crate::parse::parse_idle; use crate::types::UnsolicitedResponse; -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] use native_tls::TlsStream; #[cfg(feature = "rustls-tls")] use rustls_connector::TlsStream as RustlsStream; @@ -28,7 +28,7 @@ use std::time::Duration; /// /// ```no_run /// use imap::extensions::idle; -/// # #[cfg(feature = "tls")] +/// # #[cfg(feature = "native-tls")] /// # { /// let client = imap::ClientBuilder::new("example.com", 993).native_tls() /// .expect("Could not connect to imap server"); @@ -281,7 +281,7 @@ impl<'a> SetReadTimeout for TcpStream { } } -#[cfg(feature = "tls")] +#[cfg(feature = "native-tls")] impl<'a> SetReadTimeout for TlsStream { fn set_read_timeout(&mut self, timeout: Option) -> Result<()> { self.get_ref().set_read_timeout(timeout).map_err(Error::Io) diff --git a/src/lib.rs b/src/lib.rs index ae249b71..3860474b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ //! Below is a basic client example. See the `examples/` directory for more. //! //! ```no_run -//! # #[cfg(feature = "tls")] +//! # #[cfg(feature = "native-tls")] //! fn fetch_inbox_top() -> imap::error::Result> { //! //! let client = imap::ClientBuilder::new("imap.example.com", 993).native_tls()?; diff --git a/src/types/deleted.rs b/src/types/deleted.rs index d4b91e61..98e8ad97 100644 --- a/src/types/deleted.rs +++ b/src/types/deleted.rs @@ -18,7 +18,7 @@ use std::ops::RangeInclusive; /// /// # Examples /// ```no_run -/// # {} #[cfg(feature = "tls")] +/// # {} #[cfg(feature = "native-tls")] /// # fn main() { /// # let client = imap::ClientBuilder::new("imap.example.com", 993) /// .native_tls().unwrap(); From 37cde4aa565abc5cca74cfdf145061289fd36923 Mon Sep 17 00:00:00 2001 From: Todd Mortimer Date: Sun, 23 May 2021 15:37:01 -0400 Subject: [PATCH 2/5] fmt --- tests/imap_integration.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/imap_integration.rs b/tests/imap_integration.rs index 18292d3f..c3eea0e6 100644 --- a/tests/imap_integration.rs +++ b/tests/imap_integration.rs @@ -456,7 +456,9 @@ fn status() { // Test all valid fields except HIGHESTMODSEQ, which apparently // isn't supported by the IMAP server used for this test. - let mb = s.status("INBOX", "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)").unwrap(); + let mb = s + .status("INBOX", "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)") + .unwrap(); assert_eq!(mb.flags, Vec::new()); assert_eq!(mb.exists, 0); assert_eq!(mb.recent, 0); From de5bdd81f2ccd0dfb06f4e55d080c6ddd981d5cb Mon Sep 17 00:00:00 2001 From: Todd Mortimer Date: Sun, 23 May 2021 17:29:42 -0400 Subject: [PATCH 3/5] Rework IDLE interface to support builder pattern. --- CHANGELOG.md | 2 +- examples/idle.rs | 4 +- src/client.rs | 2 +- src/extensions/idle.rs | 103 ++++++++++++++++------------------------- 4 files changed, 43 insertions(+), 68 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 218afe18..78a828f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - MSRV increased to 1.43 for nom6 and bitvec - `expunge` and `uid_expunge` return `Result` instead of `Result>`. - - Idle `wait_keepalive_while` replaces `wait_keepalive` and takes a callback with an `UnsolicitedResponse` in parameter. + - IDLE capability now provides a builder interface. All `wait_*` functions are merged into `wait_while` which takes a callback with an `UnsolicitedResponse` in parameter. - All `Session.append_with_*` methods are obsoleted by `append` which returns now an `AppendCmd` builder. - Envelope `&'a [u8]` attributes are replaced by `Cow<'a, [u8]>`. - `Flag`, `Mailbox`, `UnsolicitedResponse` and `Error` are now declared as non exhaustive. diff --git a/examples/idle.rs b/examples/idle.rs index a329a4cf..a11faaa1 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -54,8 +54,6 @@ fn main() { imap.select(opt.mailbox).expect("Could not select mailbox"); - let idle = imap.idle().expect("Could not IDLE"); - // Implement a trivial counter that causes the IDLE callback to end the IDLE // after a fixed number of responses. // @@ -63,7 +61,7 @@ fn main() { // rest of the program and update mailbox state, decide to exit the IDLE, etc. let mut num_responses = 0; let max_responses = opt.max_responses; - let idle_result = idle.wait_keepalive_while(|response| { + let idle_result = imap.idle().wait_while(|response| { num_responses += 1; println!("IDLE response #{}: {:?}", num_responses, response); if num_responses >= max_responses { diff --git a/src/client.rs b/src/client.rs index 75baf7fd..46a98088 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1093,7 +1093,7 @@ impl Session { /// command, as specified in the base IMAP specification. /// /// See [`extensions::idle::Handle`] for details. - pub fn idle(&mut self) -> Result> { + pub fn idle(&mut self) -> extensions::idle::Handle<'_, T> { extensions::idle::Handle::make(self) } diff --git a/src/extensions/idle.rs b/src/extensions/idle.rs index 5c6f9787..dca6f93f 100644 --- a/src/extensions/idle.rs +++ b/src/extensions/idle.rs @@ -19,10 +19,11 @@ use std::time::Duration; /// specificed in [RFC 2177](https://tools.ietf.org/html/rfc2177) until the underlying server state /// changes in some way. /// -/// Each of the `wait` functions takes a callback function which receives any responses +/// The `wait_while` function takes a callback function which receives any responses /// that arrive on the channel while IDLE. The callback function implements whatever /// logic is needed to handle the IDLE response, and then returns a boolean /// to continue idling (`true`) or stop (`false`). +/// /// For users that want the IDLE to exit on any change (the behavior proior to version 3.0), /// a convenience callback function [`stop_on_any`] is provided. /// @@ -37,25 +38,24 @@ use std::time::Duration; /// imap.select("INBOX") /// .expect("Could not select mailbox"); /// -/// let idle = imap.idle().expect("Could not IDLE"); -/// -/// // Exit on any mailbox change -/// let result = idle.wait_keepalive_while(idle::stop_on_any); +/// // Exit on any mailbox change. By default, connections will be periodically +/// // refreshed in the background. +/// let result = imap.idle().wait_while(idle::stop_on_any); /// # } /// ``` /// /// Note that the server MAY consider a client inactive if it has an IDLE command running, and if /// such a server has an inactivity timeout it MAY log the client off implicitly at the end of its -/// timeout period. Because of that, clients using IDLE are advised to terminate the IDLE and -/// re-issue it at least every 29 minutes to avoid being logged off. [`Handle::wait_keepalive_while`] -/// does this. This still allows a client to receive immediate mailbox updates even though it need -/// only "poll" at half hour intervals. +/// timeout period. Because of that, clients using IDLE are advised to terminate the IDLE and +/// re-issue it at least every 29 minutes to avoid being logged off. This is done by default, but +/// can be disabled by calling [`Handle::no_keepalive`] /// /// As long as a [`Handle`] is active, the mailbox cannot be otherwise accessed. #[derive(Debug)] pub struct Handle<'a, T: Read + Write> { session: &'a mut Session, - keepalive: Duration, + timeout: Duration, + keepalive: bool, done: bool, } @@ -73,11 +73,7 @@ pub fn stop_on_any(_response: UnsolicitedResponse) -> bool { false } -/// Must be implemented for a transport in order for a `Session` using that transport to support -/// operations with timeouts. -/// -/// Examples of where this is useful is for `Handle::wait_keepalive_while` and -/// `Handle::wait_timeout_while`. +/// Must be implemented for a transport in order for a `Session` to use IDLE. pub trait SetReadTimeout { /// Set the timeout for subsequent reads to the given one. /// @@ -88,14 +84,13 @@ pub trait SetReadTimeout { } impl<'a, T: Read + Write + 'a> Handle<'a, T> { - pub(crate) fn make(session: &'a mut Session) -> Result { - let mut h = Handle { + pub(crate) fn make(session: &'a mut Session) -> Self { + Handle { session, - keepalive: Duration::from_secs(29 * 60), + timeout: Duration::from_secs(29 * 60), + keepalive: true, done: false, - }; - h.init()?; - Ok(h) + } } fn init(&mut self) -> Result<()> { @@ -132,7 +127,7 @@ impl<'a, T: Read + Write + 'a> Handle<'a, T> { /// Internal helper that doesn't consume self. /// - /// This is necessary so that we can keep using the inner `Session` in `wait_keepalive_while`. + /// This is necessary so that we can keep using the inner `Session` in `wait_while`. fn wait_inner(&mut self, reconnect: bool, mut callback: F) -> Result where F: FnMut(UnsolicitedResponse) -> bool, @@ -196,38 +191,36 @@ impl<'a, T: Read + Write + 'a> Handle<'a, T> { (_, result) => result, } } - - /// Block until the given callback returns `false`, or until a response - /// arrives that is not explicitly handled by [`UnsolicitedResponse`]. - pub fn wait_while(mut self, callback: F) -> Result<()> - where - F: FnMut(UnsolicitedResponse) -> bool, - { - self.wait_inner(true, callback).map(|_| ()) - } } impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { - /// Set the keep-alive interval to use when `wait_keepalive_while` is called. + /// Set the timeout duration on the connection. This will also set the frequency + /// at which the connection is refreshed. + /// + /// The interval defaults to 29 minutes as given in RFC 2177. + pub fn set_timeout(&mut self, interval: Duration) -> &mut Self { + self.timeout = interval; + self + } + + /// Do not continuously refresh the IDLE connection in the background. /// - /// The interval defaults to 29 minutes as dictated by RFC 2177. - pub fn set_keepalive(&mut self, interval: Duration) { - self.keepalive = interval; + /// By default, connections will periodically be refreshed in the background using the + /// timeout duration set by [`set_timeout`]. If you do not want this behaviour, call + /// this function and the connection will simply IDLE until `wait_while` returns or + /// the timeout expires. + pub fn no_keepalive(&mut self) -> &mut Self { + self.keepalive = false; + self } /// Block until the given callback returns `false`, or until a response /// arrives that is not explicitly handled by [`UnsolicitedResponse`]. - /// - /// This method differs from [`Handle::wait_while`] in that it will periodically refresh the IDLE - /// connection, to prevent the server from timing out our connection. The keepalive interval is - /// set to 29 minutes by default, as dictated by RFC 2177, but can be changed using - /// [`Handle::set_keepalive`]. - /// - /// This is the recommended method to use for waiting. - pub fn wait_keepalive_while(self, callback: F) -> Result<()> + pub fn wait_while(&mut self, callback: F) -> Result<()> where F: FnMut(UnsolicitedResponse) -> bool, { + self.init()?; // The server MAY consider a client inactive if it has an IDLE command // running, and if such a server has an inactivity timeout it MAY log // the client off implicitly at the end of its timeout period. Because @@ -235,34 +228,18 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { // re-issue it at least every 29 minutes to avoid being logged off. // This still allows a client to receive immediate mailbox updates even // though it need only "poll" at half hour intervals. - let keepalive = self.keepalive; - self.timed_wait(keepalive, true, callback).map(|_| ()) - } - - /// Block until the given given amount of time has elapsed, the given callback - /// returns `false`, or until a response arrives that is not explicitly handled - /// by [`UnsolicitedResponse`]. - pub fn wait_with_timeout_while(self, timeout: Duration, callback: F) -> Result - where - F: FnMut(UnsolicitedResponse) -> bool, - { - self.timed_wait(timeout, false, callback) + self.timed_wait(callback).map(|_| ()) } - fn timed_wait( - mut self, - timeout: Duration, - reconnect: bool, - callback: F, - ) -> Result + fn timed_wait(&mut self, callback: F) -> Result where F: FnMut(UnsolicitedResponse) -> bool, { self.session .stream .get_mut() - .set_read_timeout(Some(timeout))?; - let res = self.wait_inner(reconnect, callback); + .set_read_timeout(Some(self.timeout))?; + let res = self.wait_inner(self.keepalive, callback); let _ = self.session.stream.get_mut().set_read_timeout(None).is_ok(); res } From a24d79df0f4b9aad224b8a783ccf85b23ac80fbf Mon Sep 17 00:00:00 2001 From: Todd Mortimer Date: Tue, 25 May 2021 19:28:58 -0400 Subject: [PATCH 4/5] Normalize function names / params. Also kill timed_wait because it is only used in one place. --- examples/idle.rs | 2 +- src/extensions/idle.rs | 19 ++++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/examples/idle.rs b/examples/idle.rs index a11faaa1..2cc9685e 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -74,7 +74,7 @@ fn main() { }); match idle_result { - Ok(()) => println!("IDLE finished normally"), + Ok(reason) => println!("IDLE finished normally {:?}", reason), Err(e) => println!("IDLE finished with error {:?}", e), } diff --git a/src/extensions/idle.rs b/src/extensions/idle.rs index dca6f93f..ad2ec66f 100644 --- a/src/extensions/idle.rs +++ b/src/extensions/idle.rs @@ -48,7 +48,7 @@ use std::time::Duration; /// such a server has an inactivity timeout it MAY log the client off implicitly at the end of its /// timeout period. Because of that, clients using IDLE are advised to terminate the IDLE and /// re-issue it at least every 29 minutes to avoid being logged off. This is done by default, but -/// can be disabled by calling [`Handle::no_keepalive`] +/// can be disabled by calling [`Handle::keepalive`] /// /// As long as a [`Handle`] is active, the mailbox cannot be otherwise accessed. #[derive(Debug)] @@ -198,7 +198,7 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { /// at which the connection is refreshed. /// /// The interval defaults to 29 minutes as given in RFC 2177. - pub fn set_timeout(&mut self, interval: Duration) -> &mut Self { + pub fn timeout(&mut self, interval: Duration) -> &mut Self { self.timeout = interval; self } @@ -206,17 +206,17 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { /// Do not continuously refresh the IDLE connection in the background. /// /// By default, connections will periodically be refreshed in the background using the - /// timeout duration set by [`set_timeout`]. If you do not want this behaviour, call + /// timeout duration set by [`timeout`]. If you do not want this behaviour, call /// this function and the connection will simply IDLE until `wait_while` returns or /// the timeout expires. - pub fn no_keepalive(&mut self) -> &mut Self { - self.keepalive = false; + pub fn keepalive(&mut self, keepalive: bool) -> &mut Self { + self.keepalive = keepalive; self } /// Block until the given callback returns `false`, or until a response /// arrives that is not explicitly handled by [`UnsolicitedResponse`]. - pub fn wait_while(&mut self, callback: F) -> Result<()> + pub fn wait_while(&mut self, callback: F) -> Result where F: FnMut(UnsolicitedResponse) -> bool, { @@ -228,13 +228,6 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { // re-issue it at least every 29 minutes to avoid being logged off. // This still allows a client to receive immediate mailbox updates even // though it need only "poll" at half hour intervals. - self.timed_wait(callback).map(|_| ()) - } - - fn timed_wait(&mut self, callback: F) -> Result - where - F: FnMut(UnsolicitedResponse) -> bool, - { self.session .stream .get_mut() From 2c30de00bef05f464a37b8d446c9e9b01dee28bf Mon Sep 17 00:00:00 2001 From: Todd Mortimer Date: Tue, 25 May 2021 19:31:48 -0400 Subject: [PATCH 5/5] Fix doc link. --- src/extensions/idle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/idle.rs b/src/extensions/idle.rs index ad2ec66f..559fc777 100644 --- a/src/extensions/idle.rs +++ b/src/extensions/idle.rs @@ -206,7 +206,7 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> { /// Do not continuously refresh the IDLE connection in the background. /// /// By default, connections will periodically be refreshed in the background using the - /// timeout duration set by [`timeout`]. If you do not want this behaviour, call + /// timeout duration set by [`Handle::timeout`]. If you do not want this behaviour, call /// this function and the connection will simply IDLE until `wait_while` returns or /// the timeout expires. pub fn keepalive(&mut self, keepalive: bool) -> &mut Self {