Skip to content

Commit

Permalink
add DomainAddress to rama-net address family
Browse files Browse the repository at this point in the history
Closes #360

Signed-off-by: Hrushi20 <hrushikesh20thegreat@gmail.com>

Co-Authored-By: GlenDC <glen@plabayo.tech>
  • Loading branch information
Hrushi20 and GlenDC committed Jan 2, 2025
1 parent 641a35a commit dd4fcbd
Showing 4 changed files with 225 additions and 1 deletion.
9 changes: 8 additions & 1 deletion rama-net/src/address/authority.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{parse_utils, Domain, Host};
use super::{parse_utils, Domain, DomainAddress, Host};
use rama_core::error::{ErrorContext, OpaqueError};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::{
@@ -115,6 +115,13 @@ impl From<&SocketAddr> for Authority {
}
}

impl From<DomainAddress> for Authority {
fn from(addr: DomainAddress) -> Self {
let (domain, port) = addr.into_parts();
Self::from((domain, port))
}
}

impl fmt::Display for Authority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.host {
2 changes: 2 additions & 0 deletions rama-net/src/address/domain.rs
Original file line number Diff line number Diff line change
@@ -403,6 +403,7 @@ mod tests {
"example.com.",
".example.com.",
"rr5---sn-q4fl6n6s.video.com", // multiple dashes
"127.0.0.1",
] {
let msg = format!("to parse: {}", str);
assert_eq!(Domain::try_from(str.to_owned()).expect(msg.as_str()), str);
@@ -426,6 +427,7 @@ mod tests {
"-.-.",
"-.-.-",
".-.-",
"2001:db8:3333:4444:5555:6666:7777:8888",
"-example.com",
"local!host",
"thislabeliswaytoolongforbeingeversomethingwewishtocareabout-example.com",
211 changes: 211 additions & 0 deletions rama-net/src/address/domain_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use crate::address::{parse_utils, Domain};
use rama_core::error::{ErrorContext, OpaqueError};
use std::fmt;
use std::str::FromStr;

/// A [`Domain`] with an associated port
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DomainAddress {
domain: Domain,
port: u16,
}

impl DomainAddress {
/// Creates a new [`DomainAddress`].
pub const fn new(domain: Domain, port: u16) -> Self {
Self { domain, port }
}

/// Gets the [`Domain`] reference.
pub fn domain(&self) -> &Domain {
&self.domain
}

/// Consumes the [`DomainAddress`] and returns the [`Domain`].
pub fn into_domain(self) -> Domain {
self.domain
}

/// Gets the port.
pub fn port(&self) -> u16 {
self.port
}

/// Consume self into its parts: `(Domain, port)`
pub fn into_parts(self) -> (Domain, u16) {
(self.domain, self.port)
}
}

impl From<(Domain, u16)> for DomainAddress {
#[inline]
fn from((domain, port): (Domain, u16)) -> Self {
Self::new(domain, port)
}
}

impl fmt::Display for DomainAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.domain, self.port)
}
}

impl FromStr for DomainAddress {
type Err = OpaqueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}

impl TryFrom<&str> for DomainAddress {
type Error = OpaqueError;

fn try_from(s: &str) -> Result<Self, Self::Error> {
let (domain, port) = parse_utils::split_port_from_str(s)?;
let domain = Domain::from_str(domain)?;
Ok(Self::new(domain, port))
}
}

impl TryFrom<String> for DomainAddress {
type Error = OpaqueError;

fn try_from(s: String) -> Result<Self, Self::Error> {
let (domain, port) = parse_utils::split_port_from_str(&s)?;
let domain = Domain::from_str(domain)?;
Ok(Self::new(domain, port))
}
}
impl TryFrom<Vec<u8>> for DomainAddress {
type Error = OpaqueError;

fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
let s = String::from_utf8(bytes).context("parse domain_address from bytes")?;
s.try_into()
}
}

impl TryFrom<&[u8]> for DomainAddress {
type Error = OpaqueError;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let s = std::str::from_utf8(bytes).context("parse domain_address from bytes")?;
s.try_into()
}
}

impl serde::Serialize for DomainAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let address = self.to_string();
address.serialize(serializer)
}
}

impl<'de> serde::Deserialize<'de> for DomainAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <&'de str>::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

#[cfg(test)]
mod tests {
use super::*;

fn assert_eq(s: &str, domain_address: DomainAddress, domain: &str, port: u16) {
assert_eq!(domain_address.domain().as_str(), domain, "parsing: {}", s);
assert_eq!(domain_address.port(), port, "parsing: {}", s);
}

#[test]
fn test_valid_domain_address() {
for (s, (expected_domain, expected_port)) in [
("example.com:80", ("example.com", 80)),
("subdomain.example.com:443", ("subdomain.example.com", 443)),
("127.0.0.1:8080", ("127.0.0.1", 8080)),
] {
let msg = format!("parsing '{}'", s);

assert_eq(s, s.parse().expect(&msg), expected_domain, expected_port);
assert_eq(s, s.try_into().expect(&msg), expected_domain, expected_port);
assert_eq(
s,
s.to_owned().try_into().expect(&msg),
expected_domain,
expected_port,
);
assert_eq(
s,
s.as_bytes().try_into().expect(&msg),
expected_domain,
expected_port,
);
assert_eq(
s,
s.as_bytes().to_vec().try_into().expect(&msg),
expected_domain,
expected_port,
);
}
}

#[test]
fn test_parse_invalid() {
for s in [
"",
"-",
".",
":",
":80",
"-.",
".-",
"::1",
"127.0.0.1",
"::1:8080",
"127.0.0.1",
"[::1]",
"example",
"exa$mple.com:8080",
"2001:db8:3333:4444:5555:6666:7777:8888:8080",
"2001:db8:3333:4444:5555:6666:7777:8888",
"[2001:db8:3333:4444:5555:6666:7777:8888]",
"[2001:db8:3333:4444:5555:6666:7777:8888]:8080",
"example.com",
"example.com:",
"example.com:-1",
"example.com:999999",
"example:com",
"[127.0.0.1]:80",
"2001:db8:3333:4444:5555:6666:7777:8888:80",
] {
let msg = format!("parsing '{}'", s);
assert!(s.parse::<DomainAddress>().is_err(), "{}", msg);
assert!(DomainAddress::try_from(s).is_err(), "{}", msg);
assert!(DomainAddress::try_from(s.to_owned()).is_err(), "{}", msg);
assert!(DomainAddress::try_from(s.as_bytes()).is_err(), "{}", msg);
assert!(
DomainAddress::try_from(s.as_bytes().to_vec()).is_err(),
"{}",
msg
);
}
}

#[test]
fn test_parse_display() {
for (s, expected) in [
("example.com:80", "example.com:80"),
("subdomain.example.com:443", "subdomain.example.com:443"),
] {
let msg = format!("parsing '{}'", s);
let domain_address: DomainAddress = s.parse().expect(&msg);
assert_eq!(domain_address.to_string(), expected, "{}", msg);
}
}
}
4 changes: 4 additions & 0 deletions rama-net/src/address/mod.rs
Original file line number Diff line number Diff line change
@@ -29,3 +29,7 @@ mod parse_utils;

#[doc(inline)]
pub use proxy::ProxyAddress;

mod domain_address;
#[doc(inline)]
pub use domain_address::DomainAddress;

0 comments on commit dd4fcbd

Please sign in to comment.