mirror of
https://github.com/chatmail/core.git
synced 2026-04-20 23:16:30 +03:00
imap: disable read timeout during IDLE
Otherwise IDLE restarts every 30 seconds.
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
- python: do not pass NULL to ffi.gc if the context can't be created #3818
|
- python: do not pass NULL to ffi.gc if the context can't be created #3818
|
||||||
- Add read/write timeouts to IMAP sockets #3820
|
- Add read/write timeouts to IMAP sockets #3820
|
||||||
- Add connection timeout to IMAP sockets #3828
|
- Add connection timeout to IMAP sockets #3828
|
||||||
|
- Disable read timeout during IMAP IDLE #3826
|
||||||
|
|
||||||
|
|
||||||
## 1.102.0
|
## 1.102.0
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -123,7 +123,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "async-imap"
|
name = "async-imap"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/async-email/async-imap?branch=master#8755b666fcd8991ed4d09864b67aa88a1eb6934f"
|
source = "git+https://github.com/async-email/async-imap?branch=master#85ff7a3d9d71a3715354fabf2fc1a8d047b5710e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-native-tls",
|
"async-native-tls",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use crate::login_param::{build_tls, Socks5Config};
|
|||||||
use super::session::SessionStream;
|
use super::session::SessionStream;
|
||||||
|
|
||||||
/// IMAP write and read timeout in seconds.
|
/// IMAP write and read timeout in seconds.
|
||||||
const IMAP_TIMEOUT: Duration = Duration::from_secs(30);
|
pub(crate) const IMAP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Client {
|
pub(crate) struct Client {
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ use futures_lite::FutureExt;
|
|||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use super::session::Session;
|
use super::session::Session;
|
||||||
|
use crate::imap::client::IMAP_TIMEOUT;
|
||||||
use crate::{context::Context, scheduler::InterruptInfo};
|
use crate::{context::Context, scheduler::InterruptInfo};
|
||||||
|
|
||||||
|
const IDLE_TIMEOUT: Duration = Duration::from_secs(23 * 60);
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub async fn idle(
|
pub async fn idle(
|
||||||
mut self,
|
mut self,
|
||||||
@@ -22,7 +25,6 @@ impl Session {
|
|||||||
bail!("IMAP server does not have IDLE capability");
|
bail!("IMAP server does not have IDLE capability");
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeout = Duration::from_secs(23 * 60);
|
|
||||||
let mut info = Default::default();
|
let mut info = Default::default();
|
||||||
|
|
||||||
self.select_folder(context, watch_folder.as_deref()).await?;
|
self.select_folder(context, watch_folder.as_deref()).await?;
|
||||||
@@ -41,7 +43,12 @@ impl Session {
|
|||||||
bail!("IMAP IDLE protocol failed to init/complete: {}", err);
|
bail!("IMAP IDLE protocol failed to init/complete: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (idle_wait, interrupt) = handle.wait_with_timeout(timeout);
|
// At this point IDLE command was sent and we received a "+ idling" response. We will now
|
||||||
|
// read from the stream without getting any data for up to `IDLE_TIMEOUT`. If we don't
|
||||||
|
// disable read timeout, we would get a timeout after `IMAP_TIMEOUT`, which is a lot
|
||||||
|
// shorter than `IDLE_TIMEOUT`.
|
||||||
|
handle.as_mut().set_read_timeout(None);
|
||||||
|
let (idle_wait, interrupt) = handle.wait_with_timeout(IDLE_TIMEOUT);
|
||||||
|
|
||||||
enum Event {
|
enum Event {
|
||||||
IdleResponse(IdleResponse),
|
IdleResponse(IdleResponse),
|
||||||
@@ -90,10 +97,11 @@ impl Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let session = tokio::time::timeout(Duration::from_secs(15), handle.done())
|
let mut session = tokio::time::timeout(Duration::from_secs(15), handle.done())
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("{}: IMAP IDLE protocol timed out", folder_name))?
|
.with_context(|| format!("{}: IMAP IDLE protocol timed out", folder_name))?
|
||||||
.with_context(|| format!("{}: IMAP IDLE failed", folder_name))?;
|
.with_context(|| format!("{}: IMAP IDLE failed", folder_name))?;
|
||||||
|
session.as_mut().set_read_timeout(Some(IMAP_TIMEOUT));
|
||||||
self.inner = session;
|
self.inner = session;
|
||||||
|
|
||||||
Ok((self, info))
|
Ok((self, info))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_imap::types::Mailbox;
|
use async_imap::types::Mailbox;
|
||||||
use async_imap::Session as ImapSession;
|
use async_imap::Session as ImapSession;
|
||||||
@@ -28,14 +29,31 @@ pub(crate) struct Session {
|
|||||||
pub(crate) trait SessionStream:
|
pub(crate) trait SessionStream:
|
||||||
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + Sync + std::fmt::Debug
|
tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + Sync + std::fmt::Debug
|
||||||
{
|
{
|
||||||
|
/// Change the read timeout on the session stream.
|
||||||
|
fn set_read_timeout(&mut self, timeout: Option<Duration>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionStream for TlsStream<Box<dyn SessionStream>> {}
|
impl SessionStream for TlsStream<Box<dyn SessionStream>> {
|
||||||
impl SessionStream for TlsStream<Pin<Box<TimeoutStream<TcpStream>>>> {}
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
impl SessionStream for TlsStream<TcpStream> {}
|
self.get_mut().set_read_timeout(timeout);
|
||||||
impl SessionStream for TcpStream {}
|
}
|
||||||
impl SessionStream for Pin<Box<TimeoutStream<TcpStream>>> {}
|
}
|
||||||
impl SessionStream for Socks5Stream<TcpStream> {}
|
impl SessionStream for TlsStream<Pin<Box<TimeoutStream<TcpStream>>>> {
|
||||||
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
|
self.get_mut().set_read_timeout(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SessionStream for Pin<Box<TimeoutStream<TcpStream>>> {
|
||||||
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
|
self.as_mut().set_read_timeout_pinned(timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SessionStream for Socks5Stream<TcpStream> {
|
||||||
|
fn set_read_timeout(&mut self, _timeout: Option<Duration>) {
|
||||||
|
// FIXME: build SOCKS streams on top of TimeoutStream, not directly TcpStream,
|
||||||
|
// so we can set a read timeout for them.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for Session {
|
impl Deref for Session {
|
||||||
type Target = ImapSession<Box<dyn SessionStream>>;
|
type Target = ImapSession<Box<dyn SessionStream>>;
|
||||||
|
|||||||
Reference in New Issue
Block a user