Idle builder (#202)
This commit is contained in:
parent
07121152d7
commit
d8d69a363b
4 changed files with 42 additions and 74 deletions
|
|
@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Changed
|
### Changed
|
||||||
- MSRV increased to 1.43 for nom6 and bitvec
|
- MSRV increased to 1.43 for nom6 and bitvec
|
||||||
- `expunge` and `uid_expunge` return `Result<Deleted>` instead of `Result<Vec<u32>>`.
|
- `expunge` and `uid_expunge` return `Result<Deleted>` instead of `Result<Vec<u32>>`.
|
||||||
- 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.
|
- 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]>`.
|
- Envelope `&'a [u8]` attributes are replaced by `Cow<'a, [u8]>`.
|
||||||
- `Flag`, `Mailbox`, `UnsolicitedResponse` and `Error` are now declared as non exhaustive.
|
- `Flag`, `Mailbox`, `UnsolicitedResponse` and `Error` are now declared as non exhaustive.
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,6 @@ fn main() {
|
||||||
|
|
||||||
imap.select(opt.mailbox).expect("Could not select mailbox");
|
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
|
// Implement a trivial counter that causes the IDLE callback to end the IDLE
|
||||||
// after a fixed number of responses.
|
// 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.
|
// rest of the program and update mailbox state, decide to exit the IDLE, etc.
|
||||||
let mut num_responses = 0;
|
let mut num_responses = 0;
|
||||||
let max_responses = opt.max_responses;
|
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;
|
num_responses += 1;
|
||||||
println!("IDLE response #{}: {:?}", num_responses, response);
|
println!("IDLE response #{}: {:?}", num_responses, response);
|
||||||
if num_responses >= max_responses {
|
if num_responses >= max_responses {
|
||||||
|
|
@ -76,7 +74,7 @@ fn main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
match idle_result {
|
match idle_result {
|
||||||
Ok(()) => println!("IDLE finished normally"),
|
Ok(reason) => println!("IDLE finished normally {:?}", reason),
|
||||||
Err(e) => println!("IDLE finished with error {:?}", e),
|
Err(e) => println!("IDLE finished with error {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1093,7 +1093,7 @@ impl<T: Read + Write> Session<T> {
|
||||||
/// command, as specified in the base IMAP specification.
|
/// command, as specified in the base IMAP specification.
|
||||||
///
|
///
|
||||||
/// See [`extensions::idle::Handle`] for details.
|
/// See [`extensions::idle::Handle`] for details.
|
||||||
pub fn idle(&mut self) -> Result<extensions::idle::Handle<'_, T>> {
|
pub fn idle(&mut self) -> extensions::idle::Handle<'_, T> {
|
||||||
extensions::idle::Handle::make(self)
|
extensions::idle::Handle::make(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,11 @@ use std::time::Duration;
|
||||||
/// specificed in [RFC 2177](https://tools.ietf.org/html/rfc2177) until the underlying server state
|
/// specificed in [RFC 2177](https://tools.ietf.org/html/rfc2177) until the underlying server state
|
||||||
/// changes in some way.
|
/// 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
|
/// 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
|
/// logic is needed to handle the IDLE response, and then returns a boolean
|
||||||
/// to continue idling (`true`) or stop (`false`).
|
/// 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),
|
/// 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.
|
/// a convenience callback function [`stop_on_any`] is provided.
|
||||||
///
|
///
|
||||||
|
|
@ -37,25 +38,24 @@ use std::time::Duration;
|
||||||
/// imap.select("INBOX")
|
/// imap.select("INBOX")
|
||||||
/// .expect("Could not select mailbox");
|
/// .expect("Could not select mailbox");
|
||||||
///
|
///
|
||||||
/// let idle = imap.idle().expect("Could not IDLE");
|
/// // Exit on any mailbox change. By default, connections will be periodically
|
||||||
///
|
/// // refreshed in the background.
|
||||||
/// // Exit on any mailbox change
|
/// let result = imap.idle().wait_while(idle::stop_on_any);
|
||||||
/// let result = idle.wait_keepalive_while(idle::stop_on_any);
|
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Note that the server MAY consider a client inactive if it has an IDLE command running, and if
|
/// 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
|
/// 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
|
/// 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`]
|
/// re-issue it at least every 29 minutes to avoid being logged off. This is done by default, but
|
||||||
/// does this. This still allows a client to receive immediate mailbox updates even though it need
|
/// can be disabled by calling [`Handle::keepalive`]
|
||||||
/// only "poll" at half hour intervals.
|
|
||||||
///
|
///
|
||||||
/// As long as a [`Handle`] is active, the mailbox cannot be otherwise accessed.
|
/// As long as a [`Handle`] is active, the mailbox cannot be otherwise accessed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Handle<'a, T: Read + Write> {
|
pub struct Handle<'a, T: Read + Write> {
|
||||||
session: &'a mut Session<T>,
|
session: &'a mut Session<T>,
|
||||||
keepalive: Duration,
|
timeout: Duration,
|
||||||
|
keepalive: bool,
|
||||||
done: bool,
|
done: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,11 +73,7 @@ pub fn stop_on_any(_response: UnsolicitedResponse) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Must be implemented for a transport in order for a `Session` using that transport to support
|
/// Must be implemented for a transport in order for a `Session` to use IDLE.
|
||||||
/// operations with timeouts.
|
|
||||||
///
|
|
||||||
/// Examples of where this is useful is for `Handle::wait_keepalive_while` and
|
|
||||||
/// `Handle::wait_timeout_while`.
|
|
||||||
pub trait SetReadTimeout {
|
pub trait SetReadTimeout {
|
||||||
/// Set the timeout for subsequent reads to the given one.
|
/// 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> {
|
impl<'a, T: Read + Write + 'a> Handle<'a, T> {
|
||||||
pub(crate) fn make(session: &'a mut Session<T>) -> Result<Self> {
|
pub(crate) fn make(session: &'a mut Session<T>) -> Self {
|
||||||
let mut h = Handle {
|
Handle {
|
||||||
session,
|
session,
|
||||||
keepalive: Duration::from_secs(29 * 60),
|
timeout: Duration::from_secs(29 * 60),
|
||||||
|
keepalive: true,
|
||||||
done: false,
|
done: false,
|
||||||
};
|
}
|
||||||
h.init()?;
|
|
||||||
Ok(h)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) -> Result<()> {
|
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.
|
/// 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<F>(&mut self, reconnect: bool, mut callback: F) -> Result<WaitOutcome>
|
fn wait_inner<F>(&mut self, reconnect: bool, mut callback: F) -> Result<WaitOutcome>
|
||||||
where
|
where
|
||||||
F: FnMut(UnsolicitedResponse) -> bool,
|
F: FnMut(UnsolicitedResponse) -> bool,
|
||||||
|
|
@ -196,38 +191,36 @@ impl<'a, T: Read + Write + 'a> Handle<'a, T> {
|
||||||
(_, result) => result,
|
(_, 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<F>(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> {
|
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 dictated by RFC 2177.
|
/// The interval defaults to 29 minutes as given in RFC 2177.
|
||||||
pub fn set_keepalive(&mut self, interval: Duration) {
|
pub fn timeout(&mut self, interval: Duration) -> &mut Self {
|
||||||
self.keepalive = interval;
|
self.timeout = interval;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 [`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 {
|
||||||
|
self.keepalive = keepalive;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block until the given callback returns `false`, or until a response
|
/// Block until the given callback returns `false`, or until a response
|
||||||
/// arrives that is not explicitly handled by [`UnsolicitedResponse`].
|
/// arrives that is not explicitly handled by [`UnsolicitedResponse`].
|
||||||
///
|
pub fn wait_while<F>(&mut self, callback: F) -> Result<WaitOutcome>
|
||||||
/// 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<F>(self, callback: F) -> Result<()>
|
|
||||||
where
|
where
|
||||||
F: FnMut(UnsolicitedResponse) -> bool,
|
F: FnMut(UnsolicitedResponse) -> bool,
|
||||||
{
|
{
|
||||||
|
self.init()?;
|
||||||
// The server MAY consider a client inactive if it has an IDLE command
|
// 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
|
// 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
|
// the client off implicitly at the end of its timeout period. Because
|
||||||
|
|
@ -235,34 +228,11 @@ impl<'a, T: SetReadTimeout + Read + Write + 'a> Handle<'a, T> {
|
||||||
// re-issue it at least every 29 minutes to avoid being logged off.
|
// re-issue it at least every 29 minutes to avoid being logged off.
|
||||||
// This still allows a client to receive immediate mailbox updates even
|
// This still allows a client to receive immediate mailbox updates even
|
||||||
// though it need only "poll" at half hour intervals.
|
// 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<F>(self, timeout: Duration, callback: F) -> Result<WaitOutcome>
|
|
||||||
where
|
|
||||||
F: FnMut(UnsolicitedResponse) -> bool,
|
|
||||||
{
|
|
||||||
self.timed_wait(timeout, false, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timed_wait<F>(
|
|
||||||
mut self,
|
|
||||||
timeout: Duration,
|
|
||||||
reconnect: bool,
|
|
||||||
callback: F,
|
|
||||||
) -> Result<WaitOutcome>
|
|
||||||
where
|
|
||||||
F: FnMut(UnsolicitedResponse) -> bool,
|
|
||||||
{
|
|
||||||
self.session
|
self.session
|
||||||
.stream
|
.stream
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.set_read_timeout(Some(timeout))?;
|
.set_read_timeout(Some(self.timeout))?;
|
||||||
let res = self.wait_inner(reconnect, callback);
|
let res = self.wait_inner(self.keepalive, callback);
|
||||||
let _ = self.session.stream.get_mut().set_read_timeout(None).is_ok();
|
let _ = self.session.stream.get_mut().set_read_timeout(None).is_ok();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue