-
Notifications
You must be signed in to change notification settings - Fork 89
Implementing capability caching #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -138,6 +138,11 @@ fn validate_sequence_set( | |
| #[derive(Debug)] | ||
| pub struct Session<T: Read + Write> { | ||
| conn: Connection<T>, | ||
|
|
||
| // Capabilities are almost guaranteed to chance if encryption state or authentication state | ||
| // changes, so caching them in `Connection` is inappropiate. | ||
| capability_cache: Option<Capabilities>, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know that I believe that we shouldn't have the cache be in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had two arguments for not caching the capabilities in the
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I think that's true, I also think the current split is a foot-gun because it means we have to replicate the cache state and logic in two places. If it was on |
||
|
|
||
| pub(crate) unsolicited_responses_tx: mpsc::Sender<UnsolicitedResponse>, | ||
|
|
||
| /// Server responses that are not related to the current command. See also the note on | ||
|
|
@@ -153,6 +158,10 @@ pub struct Session<T: Read + Write> { | |
| #[derive(Debug)] | ||
| pub struct Client<T: Read + Write> { | ||
| conn: Connection<T>, | ||
|
|
||
| // Capabilities are almost guaranteed to chance if encryption state or authentication state | ||
jonhoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // changes, so caching them in `Connection` is inappropiate. | ||
| capability_cache: Option<Capabilities>, | ||
| } | ||
|
|
||
| /// The underlying primitives type. Both `Client`(unauthenticated) and `Session`(after succesful | ||
|
|
@@ -333,6 +342,7 @@ impl<T: Read + Write> Client<T> { | |
| debug: false, | ||
| greeting_read: false, | ||
| }, | ||
| capability_cache: None, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -345,6 +355,47 @@ impl<T: Read + Write> Client<T> { | |
| Ok(res) | ||
| } | ||
|
|
||
| /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a | ||
| /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as | ||
| /// one of the listed capabilities. See [`Capabilities`] for further details. | ||
| /// | ||
| /// This method will always bypass the local capabilities cache and send a `CAPABILITY` command | ||
| /// to the server. The [`Self::capabilities()`] method can be used when returning a cached | ||
| /// response is acceptable. | ||
| pub fn capabilities_refresh(&mut self) -> Result<&Capabilities> { | ||
| let (mut tx, _rx) = mpsc::channel(); | ||
| let caps = self.run_command_and_read_response("CAPABILITY") | ||
| .and_then(|lines| Capabilities::parse(lines, &mut tx))?; | ||
| self.capability_cache = Some(caps); | ||
|
|
||
| self.capability_cache.as_ref() | ||
| // This path will not be hit; if the cache is not populated the above calls will either | ||
| // populate it or return with an early error. | ||
| .ok_or_else(|| panic!("CAPABILITY call did not populate capability cache!")) | ||
jonhoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a | ||
| /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as | ||
| /// one of the listed capabilities. See [`Capabilities`] for further details. | ||
| /// | ||
| /// This function will not query the server if a set of capabilities was cached, but request | ||
| /// and cache capabilities from the server otherwise. The [`Self::capabilities_refresh`] method | ||
dequbed marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// can be used to refresh the cache by forcing a `CAPABILITY` command to be send. | ||
| pub fn capabilities_ref(&mut self) -> Result<&Capabilities> { | ||
| let (mut tx, _rx) = mpsc::channel(); | ||
| if self.capability_cache.is_none() { | ||
| let caps = self.run_command_and_read_response("CAPABILITY") | ||
| .and_then(|lines| Capabilities::parse(lines, &mut tx))?; | ||
|
|
||
| self.capability_cache = Some(caps); | ||
| } | ||
|
|
||
| self.capability_cache.as_ref() | ||
| // This path will not be hit; if the cache is not populated the above `if` will either | ||
| // populate it or return with an early error. | ||
| .ok_or_else(|| panic!("CAPABILITY call did not populate capability cache!")) | ||
| } | ||
|
|
||
| /// Log in to the IMAP server. Upon success a [`Session`](struct.Session.html) instance is | ||
| /// returned; on error the original `Client` instance is returned in addition to the error. | ||
| /// This is because `login` takes ownership of `self`, so in order to try again (e.g. after | ||
|
|
@@ -502,6 +553,7 @@ impl<T: Read + Write> Session<T> { | |
| let (tx, rx) = mpsc::channel(); | ||
| Session { | ||
| conn, | ||
| capability_cache: None, | ||
| unsolicited_responses: rx, | ||
| unsolicited_responses_tx: tx, | ||
| } | ||
|
|
@@ -771,12 +823,52 @@ impl<T: Read + Write> Session<T> { | |
| self.run_command_and_check_ok(&format!("UNSUBSCRIBE {}", quote!(mailbox.as_ref()))) | ||
| } | ||
|
|
||
| /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a | ||
| /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as | ||
| /// one of the listed capabilities. See [`Capabilities`] for further details. | ||
| /// | ||
| /// This method will always bypass the local capabilities cache and send a `CAPABILITY` command | ||
| /// to the server. The [`Self::capabilities()`] method can be used when returning a cached | ||
| /// response is acceptable. | ||
| pub fn capabilities_refresh(&mut self) -> Result<&Capabilities> { | ||
jonhoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| let caps = self.run_command_and_read_response("CAPABILITY") | ||
| .and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses_tx))?; | ||
| self.capability_cache = Some(caps); | ||
|
|
||
| self.capability_cache.as_ref() | ||
| // This path will not be hit; if the cache is not populated the above calls will either | ||
| // populate it or return with an early error. | ||
| .ok_or_else(|| panic!("CAPABILITY call did not populate capability cache!")) | ||
| } | ||
|
|
||
| /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a | ||
| /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as | ||
| /// one of the listed capabilities. See [`Capabilities`] for further details. | ||
| /// | ||
| /// This function will not query the server if a set of capabilities was cached, but request | ||
| /// and cache capabilities from the server otherwise. The [`Self::capabilities_refresh`] method | ||
| /// can be used to refresh the cache by forcing a `CAPABILITY` command to be send. | ||
| pub fn capabilities_ref(&mut self) -> Result<&Capabilities> { | ||
| if self.capability_cache.is_none() { | ||
| let caps = self.run_command_and_read_response("CAPABILITY") | ||
| .and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses_tx))?; | ||
|
|
||
| self.capability_cache = Some(caps); | ||
| } | ||
|
|
||
| self.capability_cache.as_ref() | ||
| // This path will not be hit; if the cache is not populated the above `if` will either | ||
| // populate it or return with an early error. | ||
| .ok_or_else(|| panic!("CAPABILITY call did not populate capability cache!")) | ||
| } | ||
|
|
||
| /// The [`CAPABILITY` command](https://tools.ietf.org/html/rfc3501#section-6.1.1) requests a | ||
| /// listing of capabilities that the server supports. The server will include "IMAP4rev1" as | ||
| /// one of the listed capabilities. See [`Capabilities`] for further details. | ||
| pub fn capabilities(&mut self) -> Result<Capabilities> { | ||
| self.run_command_and_read_response("CAPABILITY") | ||
| .and_then(|lines| Capabilities::parse(lines, &mut self.unsolicited_responses_tx)) | ||
| // TODO: This emulated the same behaviour as before, with each call issuing a command. | ||
| // It may be sensible to allow hitting the cache here. | ||
| self.capabilities_refresh().map(|caps| caps.clone()) | ||
jonhoo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// The [`EXPUNGE` command](https://tools.ietf.org/html/rfc3501#section-6.4.3) permanently | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.