Improved error handling (part 4)
This commit is contained in:
@@ -11,7 +11,7 @@ use store::query::Filter;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use trc::AddContext;
|
||||
|
||||
use super::{Command, ResponseCode, SerializeResponse, Session, State, StatusResponse};
|
||||
use super::{Command, ResponseCode, SerializeResponse, Session, State};
|
||||
|
||||
impl<T: SessionStream> Session<T> {
|
||||
pub async fn ingest(&mut self, bytes: &[u8]) -> SessionResult {
|
||||
@@ -31,8 +31,14 @@ impl<T: SessionStream> Session<T> {
|
||||
requests.push(request);
|
||||
}
|
||||
Err(err) => {
|
||||
let mut disconnect = err.must_disconnect();
|
||||
|
||||
if let Err(err) = self.write_error(err).await {
|
||||
tracing::error!(parent: &self.span, event = "error", error = ?err);
|
||||
disconnect = true;
|
||||
}
|
||||
|
||||
if disconnect {
|
||||
return SessionResult::Close;
|
||||
}
|
||||
}
|
||||
@@ -45,10 +51,7 @@ impl<T: SessionStream> Session<T> {
|
||||
break;
|
||||
}
|
||||
Err(receiver::Error::Error { response }) => {
|
||||
if let Err(err) = self
|
||||
.write(&StatusResponse::no(response.message).into_bytes())
|
||||
.await
|
||||
{
|
||||
if let Err(err) = self.write_error(response).await {
|
||||
tracing::error!(parent: &self.span, event = "error", error = ?err);
|
||||
return SessionResult::Close;
|
||||
}
|
||||
@@ -88,8 +91,14 @@ impl<T: SessionStream> Session<T> {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let mut disconnect = err.must_disconnect();
|
||||
|
||||
if let Err(err) = self.write_error(err).await {
|
||||
tracing::error!(parent: &self.span, event = "error", error = ?err);
|
||||
disconnect = true;
|
||||
}
|
||||
|
||||
if disconnect {
|
||||
return SessionResult::Close;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,27 +264,35 @@ pub trait SerializeResponse {
|
||||
|
||||
impl SerializeResponse for trc::Error {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let todo = "serialize messages properly in all protocols";
|
||||
let mut buf = Vec::with_capacity(64);
|
||||
buf.extend_from_slice(self.value_as_str(trc::Key::Type).unwrap_or("NO").as_bytes());
|
||||
if let Some(code) = self.value_as_str(trc::Key::Code) {
|
||||
if let Some(code) = self
|
||||
.value_as_str(trc::Key::Code)
|
||||
.or_else(|| match self.as_ref() {
|
||||
trc::Cause::Store(trc::StoreCause::NotFound) => {
|
||||
Some(ResponseCode::NonExistent.as_str())
|
||||
}
|
||||
trc::Cause::Store(_) => Some(ResponseCode::TryLater.as_str()),
|
||||
trc::Cause::Limit(trc::LimitCause::Quota) => Some(ResponseCode::Quota.as_str()),
|
||||
trc::Cause::Limit(_) => Some(ResponseCode::TryLater.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
buf.extend_from_slice(b" (");
|
||||
buf.extend_from_slice(code.as_bytes());
|
||||
buf.push(b')');
|
||||
}
|
||||
if let Some(message) = self
|
||||
let message = self
|
||||
.value_as_str(trc::Key::Details)
|
||||
.or_else(|| self.value_as_str(trc::Key::Reason))
|
||||
{
|
||||
buf.extend_from_slice(b" \"");
|
||||
for ch in message.as_bytes() {
|
||||
if [b'\"', b'\\'].contains(ch) {
|
||||
buf.push(b'\\');
|
||||
}
|
||||
buf.push(*ch);
|
||||
.unwrap_or_else(|| self.as_ref().message());
|
||||
buf.extend_from_slice(b" \"");
|
||||
for ch in message.as_bytes() {
|
||||
if [b'\"', b'\\'].contains(ch) {
|
||||
buf.push(b'\\');
|
||||
}
|
||||
buf.push(b'\"');
|
||||
buf.push(*ch);
|
||||
}
|
||||
buf.push(b'\"');
|
||||
buf.extend_from_slice(b"\r\n");
|
||||
buf
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ impl<T: SessionStream> Session<T> {
|
||||
self.jmap.is_auth_allowed_soft(&self.remote_addr).await?;
|
||||
|
||||
// Authenticate
|
||||
let mut is_totp_error = false;
|
||||
let access_token = match credentials {
|
||||
Credentials::Plain { username, secret } | Credentials::XOauth2 { username, secret } => {
|
||||
self.jmap
|
||||
@@ -81,16 +80,37 @@ impl<T: SessionStream> Session<T> {
|
||||
self.remote_addr,
|
||||
ServerProtocol::ManageSieve,
|
||||
)
|
||||
.await?
|
||||
.await
|
||||
}
|
||||
Credentials::OAuthBearer { token } => {
|
||||
let (account_id, _, _) = self
|
||||
match self
|
||||
.jmap
|
||||
.validate_access_token("access_token", &token)
|
||||
.await?;
|
||||
self.jmap.get_access_token(account_id).await?
|
||||
.await
|
||||
{
|
||||
Ok((account_id, _, _)) => self.jmap.get_access_token(account_id).await,
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
.map_err(|err| {
|
||||
if err.matches(trc::Cause::Auth(trc::AuthCause::Failed)) {
|
||||
match &self.state {
|
||||
State::NotAuthenticated { auth_failures }
|
||||
if *auth_failures < self.jmap.core.imap.max_auth_failures =>
|
||||
{
|
||||
self.state = State::NotAuthenticated {
|
||||
auth_failures: auth_failures + 1,
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return trc::AuthCause::TooManyAttempts.into_err().caused_by(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err
|
||||
})?;
|
||||
|
||||
// Enforce concurrency limits
|
||||
let in_flight = match self
|
||||
@@ -114,35 +134,7 @@ impl<T: SessionStream> Session<T> {
|
||||
in_flight,
|
||||
};
|
||||
|
||||
let todo = "implement this";
|
||||
|
||||
Ok(StatusResponse::ok("Authentication successful").into_bytes())
|
||||
/*} else {
|
||||
match &self.state {
|
||||
State::NotAuthenticated { auth_failures }
|
||||
if *auth_failures < self.jmap.core.imap.max_auth_failures =>
|
||||
{
|
||||
self.state = State::NotAuthenticated {
|
||||
auth_failures: auth_failures + 1,
|
||||
};
|
||||
Err(trc::Cause::Authentication
|
||||
.into_err()
|
||||
.details(if is_totp_error {
|
||||
"Missing TOTP code, try with 'secret$totp_code'."
|
||||
} else {
|
||||
"Authentication failed."
|
||||
}))
|
||||
}
|
||||
_ => {
|
||||
tracing::debug!(
|
||||
parent: &self.span,
|
||||
event = "disconnect",
|
||||
"Too many authentication failures, disconnecting.",
|
||||
);
|
||||
Err(StatusResponse::bye("Too many authentication failures"))
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
pub async fn handle_unauthenticate(&mut self) -> trc::Result<Vec<u8>> {
|
||||
|
||||
Reference in New Issue
Block a user