Skip to content

Commit

Permalink
Replace hyperx Header and Headers with our own implementation (le…
Browse files Browse the repository at this point in the history
…ttre#607)

* Replace hyperx Header and Headers with our own implementation

* Remove utf8_b

* Add RFC 1522 encoder

* Fix most tests

* Throw away old tests

* Header encoding tests

* Fix slicing in the middle of a char

* Content-Disposition after rebase

* Fix the rest of the tests

* Fix useless clone clippy warnings

* Remove Headers::get_raw_mut

* HeaderName::new_from_ascii fallible API

* Tidy up HeaderName::new_from_ascii_str

* HeaderName::new_from_ascii(_str) tests
  • Loading branch information
paolobarbolini authored May 1, 2021
1 parent 69334fe commit 31de9e5
Show file tree
Hide file tree
Showing 16 changed files with 1,043 additions and 491 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ tracing = { version = "0.1.16", default-features = false, features = ["std"], op

# builder
httpdate = { version = "1", optional = true }
hyperx = { version = "1", optional = true, features = ["headers"] }
mime = { version = "0.3.4", optional = true }
fastrand = { version = "1.4", optional = true }
quoted_printable = { version = "0.4", optional = true }
Expand Down Expand Up @@ -82,7 +81,7 @@ name = "transport_smtp"

[features]
default = ["smtp-transport", "native-tls", "hostname", "r2d2", "builder"]
builder = ["httpdate", "mime", "base64", "hyperx", "fastrand", "quoted_printable"]
builder = ["httpdate", "mime", "base64", "fastrand", "quoted_printable"]

# transports
file-transport = ["uuid"]
Expand Down
12 changes: 6 additions & 6 deletions src/address/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ impl TryFrom<&Headers> for Envelope {
fn try_from(headers: &Headers) -> Result<Self, Self::Error> {
let from = match headers.get::<header::Sender>() {
// If there is a Sender, use it
Some(sender) => Some(Mailbox::from(sender.clone()).email),
Some(sender) => Some(Mailbox::from(sender).email),
// ... else try From
None => match headers.get::<header::From>() {
Some(header::From(a)) => {
let from: Vec<Mailbox> = a.clone().into();
let from: Vec<Mailbox> = a.into();
if from.len() > 1 {
return Err(Error::TooManyFrom);
}
Expand All @@ -128,7 +128,7 @@ impl TryFrom<&Headers> for Envelope {

fn add_addresses_from_mailboxes(
addresses: &mut Vec<Address>,
mailboxes: Option<&Mailboxes>,
mailboxes: Option<Mailboxes>,
) {
if let Some(mailboxes) = mailboxes {
for mailbox in mailboxes.iter() {
Expand All @@ -137,9 +137,9 @@ impl TryFrom<&Headers> for Envelope {
}
}
let mut to = vec![];
add_addresses_from_mailboxes(&mut to, headers.get::<header::To>().map(|h| &h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Cc>().map(|h| &h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Bcc>().map(|h| &h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::To>().map(|h| h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Cc>().map(|h| h.0));
add_addresses_from_mailboxes(&mut to, headers.get::<header::Bcc>().map(|h| h.0));

Self::new(from, to)
}
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,11 @@ pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
#[cfg(test)]
#[cfg(feature = "builder")]
mod test {
use std::convert::TryFrom;

use super::*;
use crate::message::header::Headers;
use crate::message::{header, Mailbox, Mailboxes};
use hyperx::header::Headers;
use std::convert::TryFrom;

#[test]
fn envelope_from_headers() {
Expand Down
74 changes: 31 additions & 43 deletions src/message/header/content.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use hyperx::{
header::{Formatter as HeaderFormatter, Header, RawLike},
Error as HeaderError, Result as HyperResult,
};
use std::{
fmt::{Display, Formatter as FmtFormatter, Result as FmtResult},
str::{from_utf8, FromStr},
str::FromStr,
};

use super::{Header, HeaderName};
use crate::BoxError;

/// `Content-Transfer-Encoding` of the body
///
/// The `Message` builder takes care of choosing the most
Expand All @@ -22,9 +21,17 @@ pub enum ContentTransferEncoding {
Binary,
}

impl Default for ContentTransferEncoding {
fn default() -> Self {
ContentTransferEncoding::Base64
impl Header for ContentTransferEncoding {
fn name() -> HeaderName {
HeaderName::new_from_ascii_str("Content-Transfer-Encoding")
}

fn parse(s: &str) -> Result<Self, BoxError> {
Ok(s.parse()?)
}

fn display(&self) -> String {
self.to_string()
}
}

Expand Down Expand Up @@ -54,71 +61,52 @@ impl FromStr for ContentTransferEncoding {
}
}

impl Header for ContentTransferEncoding {
fn header_name() -> &'static str {
"Content-Transfer-Encoding"
}

// FIXME HeaderError->HeaderError, same for result
fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self>
where
T: RawLike<'a>,
Self: Sized,
{
raw.one()
.ok_or(HeaderError::Header)
.and_then(|r| from_utf8(r).map_err(|_| HeaderError::Header))
.and_then(|s| {
s.parse::<ContentTransferEncoding>()
.map_err(|_| HeaderError::Header)
})
}

fn fmt_header(&self, f: &mut HeaderFormatter<'_, '_>) -> FmtResult {
f.fmt_line(&format!("{}", self))
impl Default for ContentTransferEncoding {
fn default() -> Self {
ContentTransferEncoding::Base64
}
}

#[cfg(test)]
mod test {
use super::ContentTransferEncoding;
use hyperx::header::Headers;
use crate::message::header::{HeaderName, Headers};

#[test]
fn format_content_transfer_encoding() {
let mut headers = Headers::new();

headers.set(ContentTransferEncoding::SevenBit);

assert_eq!(
format!("{}", headers),
"Content-Transfer-Encoding: 7bit\r\n"
);
assert_eq!(headers.to_string(), "Content-Transfer-Encoding: 7bit\r\n");

headers.set(ContentTransferEncoding::Base64);

assert_eq!(
format!("{}", headers),
"Content-Transfer-Encoding: base64\r\n"
);
assert_eq!(headers.to_string(), "Content-Transfer-Encoding: base64\r\n");
}

#[test]
fn parse_content_transfer_encoding() {
let mut headers = Headers::new();

headers.set_raw("Content-Transfer-Encoding", "7bit");
headers.set_raw(
HeaderName::new_from_ascii_str("Content-Transfer-Encoding"),
"7bit".to_string(),
);

assert_eq!(
headers.get::<ContentTransferEncoding>(),
Some(&ContentTransferEncoding::SevenBit)
Some(ContentTransferEncoding::SevenBit)
);

headers.set_raw("Content-Transfer-Encoding", "base64");
headers.set_raw(
HeaderName::new_from_ascii_str("Content-Transfer-Encoding"),
"base64".to_string(),
);

assert_eq!(
headers.get::<ContentTransferEncoding>(),
Some(&ContentTransferEncoding::Base64)
Some(ContentTransferEncoding::Base64)
);
}
}
43 changes: 17 additions & 26 deletions src/message/header/content_disposition.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use std::{fmt::Result as FmtResult, str::from_utf8};

use hyperx::{
header::{Formatter as HeaderFormatter, Header, RawLike},
Error as HeaderError, Result as HyperResult,
};
use super::{Header, HeaderName};
use crate::BoxError;

/// `Content-Disposition` of an attachment
///
Expand Down Expand Up @@ -32,31 +28,23 @@ impl ContentDisposition {
}

impl Header for ContentDisposition {
fn header_name() -> &'static str {
"Content-Disposition"
fn name() -> HeaderName {
HeaderName::new_from_ascii_str("Content-Disposition")
}

// FIXME HeaderError->HeaderError, same for result
fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self>
where
T: RawLike<'a>,
Self: Sized,
{
raw.one()
.ok_or(HeaderError::Header)
.and_then(|r| from_utf8(r).map_err(|_| HeaderError::Header))
.map(|s| Self(s.into()))
fn parse(s: &str) -> Result<Self, BoxError> {
Ok(Self(s.into()))
}

fn fmt_header(&self, f: &mut HeaderFormatter<'_, '_>) -> FmtResult {
f.fmt_line(&self.0)
fn display(&self) -> String {
self.0.clone()
}
}

#[cfg(test)]
mod test {
use super::ContentDisposition;
use hyperx::header::Headers;
use crate::message::header::{HeaderName, Headers};

#[test]
fn format_content_disposition() {
Expand All @@ -78,21 +66,24 @@ mod test {
fn parse_content_disposition() {
let mut headers = Headers::new();

headers.set_raw("Content-Disposition", "inline");
headers.set_raw(
HeaderName::new_from_ascii_str("Content-Disposition"),
"inline".to_string(),
);

assert_eq!(
headers.get::<ContentDisposition>(),
Some(&ContentDisposition::inline())
Some(ContentDisposition::inline())
);

headers.set_raw(
"Content-Disposition",
"attachment; filename=\"something.txt\"",
HeaderName::new_from_ascii_str("Content-Disposition"),
"attachment; filename=\"something.txt\"".to_string(),
);

assert_eq!(
headers.get::<ContentDisposition>(),
Some(&ContentDisposition::attachment("something.txt"))
Some(ContentDisposition::attachment("something.txt"))
);
}
}
52 changes: 24 additions & 28 deletions src/message/header/content_type.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::{
error::Error as StdError,
fmt::{self, Display, Result as FmtResult},
str::{from_utf8, FromStr},
fmt::{self, Display},
str::FromStr,
};

use hyperx::{
header::{Formatter as HeaderFormatter, Header, RawLike},
Error as HeaderError, Result as HyperResult,
};
use mime::Mime;

use super::{Header, HeaderName};
use crate::BoxError;

/// `Content-Type` of the body
///
/// Defined in [RFC2045](https://tools.ietf.org/html/rfc2045#section-5)
Expand Down Expand Up @@ -42,24 +41,16 @@ impl ContentType {
}

impl Header for ContentType {
fn header_name() -> &'static str {
"Content-Type"
fn name() -> HeaderName {
HeaderName::new_from_ascii_str("Content-Type")
}

// FIXME HeaderError->HeaderError, same for result
fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self>
where
T: RawLike<'a>,
Self: Sized,
{
raw.one()
.ok_or(HeaderError::Header)
.and_then(|r| from_utf8(r).map_err(|_| HeaderError::Header))
.and_then(|s| s.parse::<Mime>().map(Self).map_err(|_| HeaderError::Header))
fn parse(s: &str) -> Result<Self, BoxError> {
Ok(Self(s.parse()?))
}

fn fmt_header(&self, f: &mut HeaderFormatter<'_, '_>) -> FmtResult {
f.fmt_line(&self.0)
fn display(&self) -> String {
self.0.to_string()
}
}

Expand Down Expand Up @@ -89,9 +80,8 @@ impl Display for ContentTypeErr {

#[cfg(test)]
mod test {
use hyperx::header::Headers;

use super::ContentType;
use crate::message::header::{HeaderName, Headers};

#[test]
fn format_content_type() {
Expand All @@ -100,14 +90,14 @@ mod test {
headers.set(ContentType::TEXT_PLAIN);

assert_eq!(
format!("{}", headers),
headers.to_string(),
"Content-Type: text/plain; charset=utf-8\r\n"
);

headers.set(ContentType::TEXT_HTML);

assert_eq!(
format!("{}", headers),
headers.to_string(),
"Content-Type: text/html; charset=utf-8\r\n"
);
}
Expand All @@ -116,12 +106,18 @@ mod test {
fn parse_content_type() {
let mut headers = Headers::new();

headers.set_raw("Content-Type", "text/plain; charset=utf-8");
headers.set_raw(
HeaderName::new_from_ascii_str("Content-Type"),
"text/plain; charset=utf-8".to_string(),
);

assert_eq!(headers.get::<ContentType>(), Some(&ContentType::TEXT_PLAIN));
assert_eq!(headers.get::<ContentType>(), Some(ContentType::TEXT_PLAIN));

headers.set_raw("Content-Type", "text/html; charset=utf-8");
headers.set_raw(
HeaderName::new_from_ascii_str("Content-Type"),
"text/html; charset=utf-8".to_string(),
);

assert_eq!(headers.get::<ContentType>(), Some(&ContentType::TEXT_HTML));
assert_eq!(headers.get::<ContentType>(), Some(ContentType::TEXT_HTML));
}
}
Loading

0 comments on commit 31de9e5

Please sign in to comment.