Skip to content

Commit

Permalink
HAProxy protocol support (closes #36)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Jan 6, 2024
1 parent 5ceecff commit 3a800af
Show file tree
Hide file tree
Showing 87 changed files with 1,129 additions and 902 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file. This projec

## Added
- ACME support for automatic TLS certificate generation and renewal.
- TLS certificate hot-reloading.
- HAProxy protocol support.

### Changed

Expand Down
29 changes: 20 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Key features:
- Integration with **OpenTelemetry** to enable monitoring, tracing, and performance analysis.
- **Secure and robust**:
- Encryption at rest with **S/MIME** or **OpenPGP**.
- Automatic TLS certificate provisioning with [ACME](https://datatracker.ietf.org/doc/html/rfc8555).
- OAuth 2.0 [authorization code](https://www.rfc-editor.org/rfc/rfc8628) and [device authorization](https://www.rfc-editor.org/rfc/rfc8628) flows.
- Access Control Lists (ACLs).
- Rate limiting.
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Stalwart Labs Ltd. <[email protected]>"]
license = "AGPL-3.0-only"
repository = "https://github.com/stalwartlabs/cli"
homepage = "https://github.com/stalwartlabs/cli"
version = "0.5.1"
version = "0.5.2"
edition = "2021"
readme = "README.md"
resolver = "2"
Expand Down
2 changes: 1 addition & 1 deletion crates/imap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "imap"
version = "0.5.1"
version = "0.5.2"
edition = "2021"
resolver = "2"

Expand Down
26 changes: 16 additions & 10 deletions crates/imap/src/core/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ use imap_proto::{
};
use jmap::auth::rate_limit::AuthenticatedLimiter;
use parking_lot::Mutex;
use tokio::io::AsyncRead;
use utils::listener::limiter::{ConcurrencyLimiter, RateLimiter};
use utils::listener::{
limiter::{ConcurrencyLimiter, RateLimiter},
SessionStream,
};

use super::{SelectedMailbox, Session, SessionData, State, IMAP};

impl<T: AsyncRead> Session<T> {
impl<T: SessionStream> Session<T> {
pub async fn ingest(&mut self, bytes: &[u8]) -> crate::Result<bool> {
/*for line in String::from_utf8_lossy(bytes).split("\r\n") {
let c = println!("{}", line);
Expand Down Expand Up @@ -221,7 +223,7 @@ pub fn group_requests(
grouped_requests
}

impl<T: AsyncRead> Session<T> {
impl<T: SessionStream> Session<T> {
fn is_allowed(&self, request: Request<Command>) -> Result<Request<Command>, StatusResponse> {
let state = &self.state;
// Rate limit request
Expand All @@ -243,7 +245,11 @@ impl<T: AsyncRead> Session<T> {
Command::Capability | Command::Noop | Command::Logout | Command::Id => Ok(request),
Command::StartTls => {
if !self.is_tls {
Ok(request)
if self.instance.acceptor.is_tls() {
Ok(request)
} else {
Err(StatusResponse::no("TLS is not available.").with_tag(request.tag))
}
} else {
Err(StatusResponse::no("Already in TLS mode.").with_tag(request.tag))
}
Expand Down Expand Up @@ -330,38 +336,38 @@ impl<T: AsyncRead> Session<T> {
}
}

impl State {
impl<T: SessionStream> State<T> {
pub fn auth_failures(&self) -> u32 {
match self {
State::NotAuthenticated { auth_failures, .. } => *auth_failures,
_ => unreachable!(),
}
}

pub fn session_data(&self) -> Arc<SessionData> {
pub fn session_data(&self) -> Arc<SessionData<T>> {
match self {
State::Authenticated { data } => data.clone(),
State::Selected { data, .. } => data.clone(),
_ => unreachable!(),
}
}

pub fn mailbox_state(&self) -> (Arc<SessionData>, Arc<SelectedMailbox>) {
pub fn mailbox_state(&self) -> (Arc<SessionData<T>>, Arc<SelectedMailbox>) {
match self {
State::Selected { data, mailbox, .. } => (data.clone(), mailbox.clone()),
_ => unreachable!(),
}
}

pub fn session_mailbox_state(&self) -> (Arc<SessionData>, Option<Arc<SelectedMailbox>>) {
pub fn session_mailbox_state(&self) -> (Arc<SessionData<T>>, Option<Arc<SelectedMailbox>>) {
match self {
State::Authenticated { data } => (data.clone(), None),
State::Selected { data, mailbox, .. } => (data.clone(), mailbox.clone().into()),
_ => unreachable!(),
}
}

pub fn select_data(&self) -> (Arc<SessionData>, Arc<SelectedMailbox>) {
pub fn select_data(&self) -> (Arc<SessionData<T>>, Arc<SelectedMailbox>) {
match self {
State::Selected { data, mailbox } => (data.clone(), mailbox.clone()),
_ => unreachable!(),
Expand Down
9 changes: 4 additions & 5 deletions crates/imap/src/core/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ use jmap_proto::{
};
use parking_lot::Mutex;
use store::query::log::{Change, Query};
use tokio::io::AsyncRead;
use utils::listener::limiter::InFlight;
use utils::listener::{limiter::InFlight, SessionStream};

use super::{Account, Mailbox, MailboxId, MailboxSync, Session, SessionData};

impl SessionData {
pub async fn new<T: AsyncRead>(
impl<T: SessionStream> SessionData<T> {
pub async fn new(
session: &Session<T>,
access_token: &AccessToken,
in_flight: InFlight,
) -> crate::Result<Self> {
let mut session = SessionData {
writer: session.writer.clone(),
stream_tx: session.stream_tx.clone(),
jmap: session.jmap.clone(),
imap: session.imap.clone(),
account_id: access_token.primary_id(),
Expand Down
3 changes: 2 additions & 1 deletion crates/imap/src/core/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ use store::{
roaring::RoaringBitmap,
write::{assert::HashedValue, BatchBuilder, F_VALUE},
};
use utils::listener::SessionStream;

use crate::core::ImapId;

use super::{Mailbox, MailboxId, MailboxState, NextMailboxState, SelectedMailbox, SessionData};

pub(crate) const MAX_RETRIES: usize = 10;

impl SessionData {
impl<T: SessionStream> SessionData<T> {
pub async fn fetch_messages(&self, mailbox: &MailboxId) -> crate::op::Result<MailboxState> {
// Obtain message ids
let message_ids = self
Expand Down
Loading

0 comments on commit 3a800af

Please sign in to comment.