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
|
||||
- MSRV increased to 1.43 for nom6 and bitvec
|
||||
- `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.
|
||||
- Envelope `&'a [u8]` attributes are replaced by `Cow<'a, [u8]>`.
|
||||
- `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");
|
||||
|
||||
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 {
|
||||
|
|
@ -76,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),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1093,7 +1093,7 @@ impl<T: Read + Write> Session<T> {
|
|||
/// command, as specified in the base IMAP specification.
|
||||
///
|
||||
/// 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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::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<T>,
|
||||
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<T>) -> Result<Self> {
|
||||
let mut h = Handle {
|
||||
pub(crate) fn make(session: &'a mut Session<T>) -> 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<F>(&mut self, reconnect: bool, mut callback: F) -> Result<WaitOutcome>
|
||||
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<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> {
|
||||
/// 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.
|
||||
pub fn set_keepalive(&mut self, interval: Duration) {
|
||||
self.keepalive = interval;
|
||||
/// The interval defaults to 29 minutes as given in RFC 2177.
|
||||
pub fn timeout(&mut self, interval: Duration) -> &mut Self {
|
||||
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
|
||||
/// 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<F>(self, callback: F) -> Result<()>
|
||||
pub fn wait_while<F>(&mut self, callback: F) -> Result<WaitOutcome>
|
||||
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,11 @@ 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<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
|
||||
.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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue