Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12dccd2bbe | ||
|
|
7899da9672 | ||
|
|
a85fdefe6f | ||
|
|
b73611c67f | ||
|
|
55cea7dbe6 | ||
|
|
d2b9d50000 | ||
|
|
3d16344d53 | ||
|
|
8873153178 | ||
|
|
b073df7666 | ||
|
|
cf6b767a9c | ||
|
|
d3d8e24824 | ||
|
|
c4df9730aa | ||
|
|
bfed19e6ad | ||
|
|
629967ac98 |
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,3 +1,51 @@
|
|||||||
|
<a name="v0.11.19"></a>
|
||||||
|
### v0.11.19 (2025-10-08)
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
* Add raw header setter to `MessageBuilder` ([#1108])
|
||||||
|
|
||||||
|
#### Misc
|
||||||
|
|
||||||
|
* Fix README example ([#1114])
|
||||||
|
* Replace custom `static_assert!` macro with `std::assert!` ([#1112])
|
||||||
|
|
||||||
|
[#1108]: https://github.com/lettre/lettre/pull/1108
|
||||||
|
[#1112]: https://github.com/lettre/lettre/pull/1112
|
||||||
|
[#1114]: https://github.com/lettre/lettre/pull/1114
|
||||||
|
|
||||||
|
<a name="v0.11.18"></a>
|
||||||
|
### v0.11.18 (2025-07-28)
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
* Allow inline attachments to be named ([#1101])
|
||||||
|
|
||||||
|
#### Misc
|
||||||
|
|
||||||
|
* Upgrade `socket2` to v0.6 ([#1098])
|
||||||
|
|
||||||
|
[#1098]: https://github.com/lettre/lettre/pull/1098
|
||||||
|
[#1101]: https://github.com/lettre/lettre/pull/1101
|
||||||
|
|
||||||
|
<a name="v0.11.17"></a>
|
||||||
|
### v0.11.17 (2025-06-06)
|
||||||
|
|
||||||
|
#### Features
|
||||||
|
|
||||||
|
* Add support for `rustls-platform-verifier` ([#1081])
|
||||||
|
|
||||||
|
#### Misc
|
||||||
|
|
||||||
|
* Change readme example to use `Mailbox::new` instead of string parsing ([#1090])
|
||||||
|
* Replace futures-util `Mutex` with std `Mutex` in `AsyncStubTransport` ([#1091])
|
||||||
|
* Avoid duplicate `abort_concurrent` implementation ([#1092])
|
||||||
|
|
||||||
|
[#1081]: https://github.com/lettre/lettre/pull/1081
|
||||||
|
[#1090]: https://github.com/lettre/lettre/pull/1090
|
||||||
|
[#1091]: https://github.com/lettre/lettre/pull/1091
|
||||||
|
[#1092]: https://github.com/lettre/lettre/pull/1092
|
||||||
|
|
||||||
<a name="v0.11.16"></a>
|
<a name="v0.11.16"></a>
|
||||||
### v0.11.16 (2025-05-12)
|
### v0.11.16 (2025-05-12)
|
||||||
|
|
||||||
|
|||||||
934
Cargo.lock
generated
934
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lettre"
|
name = "lettre"
|
||||||
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
|
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
|
||||||
version = "0.11.16"
|
version = "0.11.19"
|
||||||
description = "Email client"
|
description = "Email client"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
homepage = "https://lettre.rs"
|
homepage = "https://lettre.rs"
|
||||||
@@ -42,13 +42,14 @@ serde_json = { version = "1", optional = true }
|
|||||||
# smtp-transport
|
# smtp-transport
|
||||||
nom = { version = "8", optional = true }
|
nom = { version = "8", optional = true }
|
||||||
hostname = { version = "0.4", optional = true } # feature
|
hostname = { version = "0.4", optional = true } # feature
|
||||||
socket2 = { version = "0.5.1", optional = true }
|
socket2 = { version = "0.6", optional = true }
|
||||||
url = { version = "2.4", optional = true }
|
url = { version = "2.4", optional = true }
|
||||||
percent-encoding = { version = "2.3", optional = true }
|
percent-encoding = { version = "2.3", optional = true }
|
||||||
|
|
||||||
## tls
|
## tls
|
||||||
native-tls = { version = "0.2.9", optional = true } # feature
|
native-tls = { version = "0.2.9", optional = true } # feature
|
||||||
rustls = { version = "0.23.18", default-features = false, features = ["logging", "std", "tls12"], optional = true }
|
rustls = { version = "0.23.18", default-features = false, features = ["logging", "std", "tls12"], optional = true }
|
||||||
|
rustls-platform-verifier = { version = "0.6.0", optional = true }
|
||||||
rustls-native-certs = { version = "0.8", optional = true }
|
rustls-native-certs = { version = "0.8", optional = true }
|
||||||
webpki-roots = { version = "1.0.0", optional = true }
|
webpki-roots = { version = "1.0.0", optional = true }
|
||||||
boring = { version = "4", optional = true }
|
boring = { version = "4", optional = true }
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -28,8 +28,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://deps.rs/crate/lettre/0.11.16">
|
<a href="https://deps.rs/crate/lettre/0.11.19">
|
||||||
<img src="https://deps.rs/crate/lettre/0.11.16/status.svg"
|
<img src="https://deps.rs/crate/lettre/0.11.19/status.svg"
|
||||||
alt="dependency status" />
|
alt="dependency status" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,15 +67,15 @@ lettre = "0.11"
|
|||||||
```
|
```
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
use lettre::message::header::ContentType;
|
use lettre::message::{Mailbox, header::ContentType};
|
||||||
use lettre::transport::smtp::authentication::Credentials;
|
use lettre::transport::smtp::authentication::Credentials;
|
||||||
use lettre::{Message, SmtpTransport, Transport};
|
use lettre::{Message, SmtpTransport, Transport};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let email = Message::builder()
|
let email = Message::builder()
|
||||||
.from("NoBody <nobody@domain.tld>".parse().unwrap())
|
.from(Mailbox::new(Some("NoBody".to_owned()), "nobody@domain.tld".parse().unwrap()))
|
||||||
.reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
|
.reply_to(Mailbox::new(Some("Yuin".to_owned()), "yuin@domain.tld".parse().unwrap()))
|
||||||
.to("Hei <hei@domain.tld>".parse().unwrap())
|
.to(Mailbox::new(Some("Hei".to_owned()), "hei@domain.tld".parse().unwrap()))
|
||||||
.subject("Happy new year")
|
.subject("Happy new year")
|
||||||
.header(ContentType::TEXT_PLAIN)
|
.header(ContentType::TEXT_PLAIN)
|
||||||
.body(String::from("Be happy!"))
|
.body(String::from("Be happy!"))
|
||||||
|
|||||||
27
src/lib.rs
27
src/lib.rs
@@ -93,17 +93,20 @@
|
|||||||
//! When the `rustls` feature is enabled, one of the following verification backends
|
//! When the `rustls` feature is enabled, one of the following verification backends
|
||||||
//! MUST also be enabled.
|
//! MUST also be enabled.
|
||||||
//!
|
//!
|
||||||
//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`])
|
//! * **rustls-platform-verifier**: verify TLS certificate using the OS's native certificate store (see [`rustls-platform-verifier`])
|
||||||
|
//! * **rustls-native-certs**: verify TLS certificates using the platform's native certificate store (see [`rustls-native-certs`]) - when in doubt use `rustls-platform-verifier`
|
||||||
//! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
|
//! * **webpki-roots**: verify TLS certificates against Mozilla's root certificates (see [`webpki-roots`])
|
||||||
//!
|
//!
|
||||||
//! For the `rustls-native-certs` backend to work correctly, the following packages
|
//! The following packages will need to be installed in order for the build
|
||||||
//! will need to be installed in order for the build stage and the compiled program
|
//! stage and the compiled program to run properly.
|
||||||
//! to run properly.
|
|
||||||
//!
|
//!
|
||||||
//! | Distro | Build-time packages | Runtime packages |
|
//! | Verification backend | Distro | Build-time packages | Runtime packages |
|
||||||
//! | ------------ | -------------------------- | ---------------------------- |
|
//! | --------------------- | ------------ | -------------------------- | ---------------------------- |
|
||||||
//! | Debian | none | `ca-certificates` |
|
//! | `rustls-platform-verifier` | Debian | none | `ca-certificates` |
|
||||||
//! | Alpine Linux | none | `ca-certificates` |
|
//! | `rustls-platform-verifier` | Alpine Linux | none | `ca-certificates` |
|
||||||
|
//! | `rustls-native-certs` | Debian | none | `ca-certificates` |
|
||||||
|
//! | `rustls-native-certs` | Alpine Linux | none | `ca-certificates` |
|
||||||
|
//! | `webpki-roots` | any | none | none |
|
||||||
//!
|
//!
|
||||||
//! ### Sendmail transport
|
//! ### Sendmail transport
|
||||||
//!
|
//!
|
||||||
@@ -151,6 +154,7 @@
|
|||||||
//! [AWS-LC]: https://github.com/aws/aws-lc
|
//! [AWS-LC]: https://github.com/aws/aws-lc
|
||||||
//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
|
//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs
|
||||||
//! [`ring`]: https://crates.io/crates/ring
|
//! [`ring`]: https://crates.io/crates/ring
|
||||||
|
//! [`rustls-platform-verifier`]: https://crates.io/crates/rustls-platform-verifier
|
||||||
//! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
|
//! [`rustls-native-certs`]: https://crates.io/crates/rustls-native-certs
|
||||||
//! [`webpki-roots`]: https://crates.io/crates/webpki-roots
|
//! [`webpki-roots`]: https://crates.io/crates/webpki-roots
|
||||||
//! [Tokio 1.x]: https://docs.rs/tokio/1
|
//! [Tokio 1.x]: https://docs.rs/tokio/1
|
||||||
@@ -158,7 +162,7 @@
|
|||||||
//! [mime 0.3]: https://docs.rs/mime/0.3
|
//! [mime 0.3]: https://docs.rs/mime/0.3
|
||||||
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
|
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.16")]
|
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.19")]
|
||||||
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
|
||||||
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
|
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
@@ -208,12 +212,13 @@ mod compiletime_checks {
|
|||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "rustls",
|
feature = "rustls",
|
||||||
|
not(feature = "rustls-platform-verifier"),
|
||||||
not(feature = "rustls-native-certs"),
|
not(feature = "rustls-native-certs"),
|
||||||
not(feature = "webpki-roots")
|
not(feature = "webpki-roots")
|
||||||
))]
|
))]
|
||||||
compile_error!(
|
compile_error!(
|
||||||
"feature `rustls` also requires either the `rustls-native-certs` or the `webpki-roots` feature to
|
"feature `rustls` also requires either the `rustls-platform-verifier`, the `rustls-native-certs`
|
||||||
be enabled"
|
or the `webpki-roots` feature to be enabled"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(all(feature = "native-tls", feature = "boring-tls"))]
|
#[cfg(all(feature = "native-tls", feature = "boring-tls"))]
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ enum Disposition {
|
|||||||
/// File name
|
/// File name
|
||||||
Attached(String),
|
Attached(String),
|
||||||
/// Content id
|
/// Content id
|
||||||
Inline(String),
|
Inline {
|
||||||
|
content_id: String,
|
||||||
|
name: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attachment {
|
impl Attachment {
|
||||||
@@ -81,7 +84,50 @@ impl Attachment {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn new_inline(content_id: String) -> Self {
|
pub fn new_inline(content_id: String) -> Self {
|
||||||
Attachment {
|
Attachment {
|
||||||
disposition: Disposition::Inline(content_id),
|
disposition: Disposition::Inline {
|
||||||
|
content_id,
|
||||||
|
name: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new inline attachment giving it a name
|
||||||
|
///
|
||||||
|
/// This attachment should be displayed inline into the message
|
||||||
|
/// body:
|
||||||
|
///
|
||||||
|
/// ```html
|
||||||
|
/// <img src="cid:123">
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::error::Error;
|
||||||
|
/// use std::fs;
|
||||||
|
///
|
||||||
|
/// use lettre::message::{header::ContentType, Attachment};
|
||||||
|
///
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
/// let content_id = String::from("123");
|
||||||
|
/// let file_name = String::from("image.jpg");
|
||||||
|
/// # if false {
|
||||||
|
/// let filebody = fs::read(&file_name)?;
|
||||||
|
/// # }
|
||||||
|
/// # let filebody = fs::read("docs/lettre.png")?;
|
||||||
|
/// let content_type = ContentType::parse("image/jpeg").unwrap();
|
||||||
|
/// let attachment =
|
||||||
|
/// Attachment::new_inline_with_name(content_id, file_name).body(filebody, content_type);
|
||||||
|
///
|
||||||
|
/// // The image `attachment` will display inline into the email.
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn new_inline_with_name(content_id: String, name: String) -> Self {
|
||||||
|
Attachment {
|
||||||
|
disposition: Disposition::Inline {
|
||||||
|
content_id,
|
||||||
|
name: Some(name),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,9 +141,18 @@ impl Attachment {
|
|||||||
Disposition::Attached(filename) => {
|
Disposition::Attached(filename) => {
|
||||||
builder.header(header::ContentDisposition::attachment(&filename))
|
builder.header(header::ContentDisposition::attachment(&filename))
|
||||||
}
|
}
|
||||||
Disposition::Inline(content_id) => builder
|
Disposition::Inline {
|
||||||
|
content_id,
|
||||||
|
name: None,
|
||||||
|
} => builder
|
||||||
.header(header::ContentId::from(format!("<{content_id}>")))
|
.header(header::ContentId::from(format!("<{content_id}>")))
|
||||||
.header(header::ContentDisposition::inline()),
|
.header(header::ContentDisposition::inline()),
|
||||||
|
Disposition::Inline {
|
||||||
|
content_id,
|
||||||
|
name: Some(name),
|
||||||
|
} => builder
|
||||||
|
.header(header::ContentId::from(format!("<{content_id}>")))
|
||||||
|
.header(header::ContentDisposition::inline_with_name(&name)),
|
||||||
};
|
};
|
||||||
builder = builder.header(content_type);
|
builder = builder.header(content_type);
|
||||||
builder.body(content)
|
builder.body(content)
|
||||||
@@ -142,4 +197,24 @@ mod tests {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attachment_inline_with_name() {
|
||||||
|
let id = String::from("id");
|
||||||
|
let name = String::from("test");
|
||||||
|
let part = super::Attachment::new_inline_with_name(id, name).body(
|
||||||
|
String::from("Hello world!"),
|
||||||
|
ContentType::parse("text/plain").unwrap(),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&String::from_utf8_lossy(&part.formatted()),
|
||||||
|
concat!(
|
||||||
|
"Content-ID: <id>\r\n",
|
||||||
|
"Content-Disposition: inline; filename=\"test\"\r\n",
|
||||||
|
"Content-Type: text/plain\r\n",
|
||||||
|
"Content-Transfer-Encoding: 7bit\r\n\r\n",
|
||||||
|
"Hello world!\r\n"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,21 +186,15 @@ impl HeaderName {
|
|||||||
|
|
||||||
/// Creates a new header name, panics on invalid name
|
/// Creates a new header name, panics on invalid name
|
||||||
pub const fn new_from_ascii_str(ascii: &'static str) -> Self {
|
pub const fn new_from_ascii_str(ascii: &'static str) -> Self {
|
||||||
macro_rules! static_assert {
|
assert!(!ascii.is_empty());
|
||||||
($condition:expr) => {
|
assert!(ascii.len() <= 76);
|
||||||
let _ = [()][(!($condition)) as usize];
|
assert!(ascii.is_ascii());
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assert!(!ascii.is_empty());
|
|
||||||
static_assert!(ascii.len() <= 76);
|
|
||||||
|
|
||||||
let bytes = ascii.as_bytes();
|
let bytes = ascii.as_bytes();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < bytes.len() {
|
while i < bytes.len() {
|
||||||
static_assert!(bytes[i].is_ascii());
|
assert!(bytes[i] != b' ');
|
||||||
static_assert!(bytes[i] != b' ');
|
assert!(bytes[i] != b':');
|
||||||
static_assert!(bytes[i] != b':');
|
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ mod mimebody;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
address::Envelope,
|
address::Envelope,
|
||||||
message::header::{ContentTransferEncoding, Header, Headers, MailboxesHeader},
|
message::header::{ContentTransferEncoding, Header, HeaderValue, Headers, MailboxesHeader},
|
||||||
Error as EmailError,
|
Error as EmailError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -369,6 +369,12 @@ impl MessageBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set raw custom header to message
|
||||||
|
pub fn raw_header(mut self, raw_header: HeaderValue) -> Self {
|
||||||
|
self.headers.insert_raw(raw_header);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Add mailbox to header
|
/// Add mailbox to header
|
||||||
pub fn mailbox<H: Header + MailboxesHeader>(self, header: H) -> Self {
|
pub fn mailbox<H: Header + MailboxesHeader>(self, header: H) -> Self {
|
||||||
match self.headers.get::<H>() {
|
match self.headers.get::<H>() {
|
||||||
@@ -711,7 +717,10 @@ mod test {
|
|||||||
.header(header::To(
|
.header(header::To(
|
||||||
vec!["Pony O.P. <pony@domain.tld>".parse().unwrap()].into(),
|
vec!["Pony O.P. <pony@domain.tld>".parse().unwrap()].into(),
|
||||||
))
|
))
|
||||||
.header(header::Subject::from(String::from("яңа ел белән!")))
|
.raw_header(header::HeaderValue::new(
|
||||||
|
header::HeaderName::new_from_ascii_str("Subject"),
|
||||||
|
"яңа ел белән!".to_owned(),
|
||||||
|
))
|
||||||
.body(String::from("Happy new year!"))
|
.body(String::from("Happy new year!"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -164,8 +164,9 @@ pub enum CertificateStore {
|
|||||||
/// For native-tls, this will use the system certificate store on Windows, the keychain on
|
/// For native-tls, this will use the system certificate store on Windows, the keychain on
|
||||||
/// macOS, and OpenSSL directories on Linux (usually `/etc/ssl`).
|
/// macOS, and OpenSSL directories on Linux (usually `/etc/ssl`).
|
||||||
///
|
///
|
||||||
/// For rustls, this will also use the system store if the `rustls-native-certs` feature is
|
/// For rustls, this will use the system certificate verifier if the `rustls-platform-verifier`
|
||||||
/// enabled, or will fall back to `webpki-roots`.
|
/// feature is enabled. If the `rustls-native-certs` feature is enabled, system certificate
|
||||||
|
/// store will be used. Otherwise, it will fall back to `webpki-roots`.
|
||||||
///
|
///
|
||||||
/// The boring-tls backend uses the same logic as OpenSSL on all platforms.
|
/// The boring-tls backend uses the same logic as OpenSSL on all platforms.
|
||||||
#[default]
|
#[default]
|
||||||
@@ -259,6 +260,8 @@ impl TlsParametersBuilder {
|
|||||||
|
|
||||||
/// Controls whether certificates with an invalid hostname are accepted
|
/// Controls whether certificates with an invalid hostname are accepted
|
||||||
///
|
///
|
||||||
|
/// This option is silently disabled when using `rustls-platform-verifier`.
|
||||||
|
///
|
||||||
/// Defaults to `false`.
|
/// Defaults to `false`.
|
||||||
///
|
///
|
||||||
/// # Warning
|
/// # Warning
|
||||||
@@ -461,7 +464,10 @@ impl TlsParametersBuilder {
|
|||||||
// Build TLS config
|
// Build TLS config
|
||||||
let mut root_cert_store = RootCertStore::empty();
|
let mut root_cert_store = RootCertStore::empty();
|
||||||
|
|
||||||
#[cfg(feature = "rustls-native-certs")]
|
#[cfg(all(
|
||||||
|
not(feature = "rustls-platform-verifier"),
|
||||||
|
feature = "rustls-native-certs"
|
||||||
|
))]
|
||||||
fn load_native_roots(store: &mut RootCertStore) {
|
fn load_native_roots(store: &mut RootCertStore) {
|
||||||
let rustls_native_certs::CertificateResult { certs, errors, .. } =
|
let rustls_native_certs::CertificateResult { certs, errors, .. } =
|
||||||
rustls_native_certs::load_native_certs();
|
rustls_native_certs::load_native_certs();
|
||||||
@@ -481,11 +487,26 @@ impl TlsParametersBuilder {
|
|||||||
store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "rustls-platform-verifier"), allow(unused_mut))]
|
||||||
|
let mut extra_roots = None::<Vec<CertificateDer<'static>>>;
|
||||||
match self.cert_store {
|
match self.cert_store {
|
||||||
CertificateStore::Default => {
|
CertificateStore::Default => {
|
||||||
#[cfg(feature = "rustls-native-certs")]
|
#[cfg(feature = "rustls-platform-verifier")]
|
||||||
|
{
|
||||||
|
extra_roots = Some(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
not(feature = "rustls-platform-verifier"),
|
||||||
|
feature = "rustls-native-certs"
|
||||||
|
))]
|
||||||
load_native_roots(&mut root_cert_store);
|
load_native_roots(&mut root_cert_store);
|
||||||
#[cfg(all(not(feature = "rustls-native-certs"), feature = "webpki-roots"))]
|
|
||||||
|
#[cfg(all(
|
||||||
|
not(feature = "rustls-platform-verifier"),
|
||||||
|
not(feature = "rustls-native-certs"),
|
||||||
|
feature = "webpki-roots"
|
||||||
|
))]
|
||||||
load_webpki_roots(&mut root_cert_store);
|
load_webpki_roots(&mut root_cert_store);
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
|
#[cfg(all(feature = "rustls", feature = "webpki-roots"))]
|
||||||
@@ -496,11 +517,17 @@ impl TlsParametersBuilder {
|
|||||||
}
|
}
|
||||||
for cert in self.root_certs {
|
for cert in self.root_certs {
|
||||||
for rustls_cert in cert.rustls {
|
for rustls_cert in cert.rustls {
|
||||||
|
#[cfg(feature = "rustls-platform-verifier")]
|
||||||
|
if let Some(extra_roots) = &mut extra_roots {
|
||||||
|
extra_roots.push(rustls_cert.clone());
|
||||||
|
}
|
||||||
root_cert_store.add(rustls_cert).map_err(error::tls)?;
|
root_cert_store.add(rustls_cert).map_err(error::tls)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tls = if self.accept_invalid_certs || self.accept_invalid_hostnames {
|
let tls = if self.accept_invalid_certs
|
||||||
|
|| (extra_roots.is_none() && self.accept_invalid_hostnames)
|
||||||
|
{
|
||||||
let verifier = InvalidCertsVerifier {
|
let verifier = InvalidCertsVerifier {
|
||||||
ignore_invalid_hostnames: self.accept_invalid_hostnames,
|
ignore_invalid_hostnames: self.accept_invalid_hostnames,
|
||||||
ignore_invalid_certs: self.accept_invalid_certs,
|
ignore_invalid_certs: self.accept_invalid_certs,
|
||||||
@@ -509,8 +536,24 @@ impl TlsParametersBuilder {
|
|||||||
};
|
};
|
||||||
tls.dangerous()
|
tls.dangerous()
|
||||||
.with_custom_certificate_verifier(Arc::new(verifier))
|
.with_custom_certificate_verifier(Arc::new(verifier))
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "rustls-platform-verifier")]
|
||||||
|
if let Some(extra_roots) = extra_roots {
|
||||||
|
tls.dangerous().with_custom_certificate_verifier(Arc::new(
|
||||||
|
rustls_platform_verifier::Verifier::new_with_extra_roots(
|
||||||
|
extra_roots,
|
||||||
|
crypto_provider,
|
||||||
|
)
|
||||||
|
.map_err(error::tls)?,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
tls.with_root_certificates(root_cert_store)
|
tls.with_root_certificates(root_cert_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rustls-platform-verifier"))]
|
||||||
|
{
|
||||||
|
tls.with_root_certificates(root_cert_store)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tls = if let Some(identity) = self.identity {
|
let tls = if let Some(identity) = self.identity {
|
||||||
|
|||||||
@@ -149,11 +149,7 @@ impl<E: Executor> Pool<E> {
|
|||||||
pub(crate) async fn shutdown(&self) {
|
pub(crate) async fn shutdown(&self) {
|
||||||
let connections = { self.connections.lock().await.take() };
|
let connections = { self.connections.lock().await.take() };
|
||||||
if let Some(connections) = connections {
|
if let Some(connections) = connections {
|
||||||
stream::iter(connections)
|
abort_concurrent(connections.into_iter().map(ParkedConnection::unpark)).await;
|
||||||
.for_each_concurrent(8, |conn| async move {
|
|
||||||
conn.unpark().abort().await;
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(handle) = self.handle.get() {
|
if let Some(handle) = self.handle.get() {
|
||||||
|
|||||||
@@ -43,13 +43,11 @@
|
|||||||
use std::{
|
use std::{
|
||||||
error::Error as StdError,
|
error::Error as StdError,
|
||||||
fmt,
|
fmt,
|
||||||
sync::{Arc, Mutex as StdMutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
|
||||||
use futures_util::lock::Mutex as FuturesMutex;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
||||||
use crate::AsyncTransport;
|
use crate::AsyncTransport;
|
||||||
@@ -72,7 +70,7 @@ impl StdError for Error {}
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StubTransport {
|
pub struct StubTransport {
|
||||||
response: Result<(), Error>,
|
response: Result<(), Error>,
|
||||||
message_log: Arc<StdMutex<Vec<(Envelope, String)>>>,
|
message_log: Arc<Mutex<Vec<(Envelope, String)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This transport logs messages and always returns the given response
|
/// This transport logs messages and always returns the given response
|
||||||
@@ -81,7 +79,7 @@ pub struct StubTransport {
|
|||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio1", feature = "async-std1"))))]
|
||||||
pub struct AsyncStubTransport {
|
pub struct AsyncStubTransport {
|
||||||
response: Result<(), Error>,
|
response: Result<(), Error>,
|
||||||
message_log: Arc<FuturesMutex<Vec<(Envelope, String)>>>,
|
message_log: Arc<Mutex<Vec<(Envelope, String)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StubTransport {
|
impl StubTransport {
|
||||||
@@ -89,7 +87,7 @@ impl StubTransport {
|
|||||||
pub fn new(response: Result<(), Error>) -> Self {
|
pub fn new(response: Result<(), Error>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
response,
|
response,
|
||||||
message_log: Arc::new(StdMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +95,7 @@ impl StubTransport {
|
|||||||
pub fn new_ok() -> Self {
|
pub fn new_ok() -> Self {
|
||||||
Self {
|
Self {
|
||||||
response: Ok(()),
|
response: Ok(()),
|
||||||
message_log: Arc::new(StdMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +103,7 @@ impl StubTransport {
|
|||||||
pub fn new_error() -> Self {
|
pub fn new_error() -> Self {
|
||||||
Self {
|
Self {
|
||||||
response: Err(Error),
|
response: Err(Error),
|
||||||
message_log: Arc::new(StdMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +122,7 @@ impl AsyncStubTransport {
|
|||||||
pub fn new(response: Result<(), Error>) -> Self {
|
pub fn new(response: Result<(), Error>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
response,
|
response,
|
||||||
message_log: Arc::new(FuturesMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +130,7 @@ impl AsyncStubTransport {
|
|||||||
pub fn new_ok() -> Self {
|
pub fn new_ok() -> Self {
|
||||||
Self {
|
Self {
|
||||||
response: Ok(()),
|
response: Ok(()),
|
||||||
message_log: Arc::new(FuturesMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,14 +138,14 @@ impl AsyncStubTransport {
|
|||||||
pub fn new_error() -> Self {
|
pub fn new_error() -> Self {
|
||||||
Self {
|
Self {
|
||||||
response: Err(Error),
|
response: Err(Error),
|
||||||
message_log: Arc::new(FuturesMutex::new(vec![])),
|
message_log: Arc::new(Mutex::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return all logged messages sent using [`AsyncTransport::send_raw`]
|
/// Return all logged messages sent using [`AsyncTransport::send_raw`]
|
||||||
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
|
||||||
pub async fn messages(&self) -> Vec<(Envelope, String)> {
|
pub async fn messages(&self) -> Vec<(Envelope, String)> {
|
||||||
self.message_log.lock().await.clone()
|
self.message_log.lock().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +171,7 @@ impl AsyncTransport for AsyncStubTransport {
|
|||||||
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
async fn send_raw(&self, envelope: &Envelope, email: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||||
self.message_log
|
self.message_log
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.unwrap()
|
||||||
.push((envelope.clone(), String::from_utf8_lossy(email).into()));
|
.push((envelope.clone(), String::from_utf8_lossy(email).into()));
|
||||||
self.response
|
self.response
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user