Skip to content

Commit

Permalink
start defining UA profile types and prepare for encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
GlenDC committed Jan 21, 2025
1 parent 6969d6b commit bff7d85
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 2 deletions.
5 changes: 5 additions & 0 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ http-full = ["http", "tcp", "dep:rama-http-backend", "dep:rama-http-core"]
proxy = ["dep:rama-proxy"]
haproxy = ["dep:rama-haproxy"]
ua = ["dep:rama-ua"]
ua-memory-db = ["ua", "rama-ua/memory-db"]
proxy-memory-db = ["proxy", "rama-proxy/memory-db", "rama-net/venndb"]
proxy-live-update = ["proxy", "rama-proxy/live-update"]
proxy-csv = ["proxy", "rama-proxy/csv"]
Expand Down
1 change: 1 addition & 0 deletions rama-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ venndb = { workspace = true, optional = true }
itertools = { workspace = true }
nom = { workspace = true }
quickcheck = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio-test = { workspace = true }

Expand Down
6 changes: 4 additions & 2 deletions rama-net/src/tls/client/hello/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use serde::{Deserialize, Serialize};

use crate::address::Host;
use crate::tls::{
enums::CompressionAlgorithm, ApplicationProtocol, CipherSuite, ECPointFormat, ExtensionId,
Expand All @@ -10,7 +12,7 @@ mod rustls;
#[cfg(feature = "boring")]
mod boring;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
/// When a client first connects to a server, it is required to send
/// the ClientHello as its first message.
///
Expand Down Expand Up @@ -125,7 +127,7 @@ impl ClientHello {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
/// Extensions that can be set in a [`ClientHello`] message by a TLS client.
///
/// While its name may infer that an extension is by definition optional,
Expand Down
111 changes: 111 additions & 0 deletions rama-net/src/tls/enums/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,27 @@ macro_rules! enum_builder {
::std::fmt::UpperHex::fmt(&u8::from(*self), f)
}
}

impl ::serde::Serialize for $enum_name {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
u8::from(*self).serialize(serializer)
}
}

impl<'de> ::serde::Deserialize<'de> for $enum_name {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
let n = u8::deserialize(deserializer)?;
Ok(n.into())
}
}
};
(
$(#[$comment:meta])*
Expand Down Expand Up @@ -133,6 +154,27 @@ macro_rules! enum_builder {
::std::fmt::UpperHex::fmt(&u16::from(*self), f)
}
}

impl ::serde::Serialize for $enum_name {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
u16::from(*self).serialize(serializer)
}
}

impl<'de> ::serde::Deserialize<'de> for $enum_name {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
let n = u16::deserialize(deserializer)?;
Ok(n.into())
}
}
};
(
$(#[$comment:meta])*
Expand Down Expand Up @@ -256,6 +298,34 @@ macro_rules! enum_builder {
}
}
}

impl ::serde::Serialize for $enum_name {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
match self {
$( $enum_name::$enum_var => {
$enum_val.serialize(serializer)
}),*
,$enum_name::Unknown(x) => {
x.serialize(serializer)
}
}
}
}

impl<'de> ::serde::Deserialize<'de> for $enum_name {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
let b = <::std::borrow::Cow<'de, [u8]>>::deserialize(deserializer)?;
Ok(b.as_ref().into())
}
}
};
}

Expand Down Expand Up @@ -1021,4 +1091,45 @@ mod tests {
assert_eq!(12, r.position());
assert_eq!(&INPUT.as_bytes()[3..12], b"\x08http/1.1");
}

#[test]
fn test_enum_u8_serialize_deserialize() {
let p: ECPointFormat = serde_json::from_str(
&serde_json::to_string(&ECPointFormat::ANSIX962CompressedChar2).unwrap(),
)
.unwrap();
assert_eq!(ECPointFormat::ANSIX962CompressedChar2, p);

let p: ECPointFormat =
serde_json::from_str(&serde_json::to_string(&ECPointFormat::from(42u8)).unwrap())
.unwrap();
assert_eq!(ECPointFormat::from(42u8), p);
}

#[test]
fn test_enum_u16_serialize_deserialize() {
let p: SupportedGroup =
serde_json::from_str(&serde_json::to_string(&SupportedGroup::BRAINPOOLP384R1).unwrap())
.unwrap();
assert_eq!(SupportedGroup::BRAINPOOLP384R1, p);

let p: SupportedGroup =
serde_json::from_str(&serde_json::to_string(&SupportedGroup::from(0xffffu16)).unwrap())
.unwrap();
assert_eq!(SupportedGroup::from(0xffffu16), p);
}

#[test]
fn test_enum_bytes_serialize_deserialize() {
let p: ApplicationProtocol =
serde_json::from_str(&serde_json::to_string(&ApplicationProtocol::HTTP_3).unwrap())
.unwrap();
assert_eq!(ApplicationProtocol::HTTP_3, p);

let p: ApplicationProtocol = serde_json::from_str(
&serde_json::to_string(&ApplicationProtocol::from(b"foobar")).unwrap(),
)
.unwrap();
assert_eq!(ApplicationProtocol::from(b"foobar"), p);
}
}
9 changes: 9 additions & 0 deletions rama-ua/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,19 @@ rust-version = { workspace = true }
[lints]
workspace = true

[features]
default = []
memory-db = ["dep:venndb"]
tls = ["dep:rama-net", "rama-net/tls"]

[dependencies]
bytes = { workspace = true }
rama-core = { version = "0.2.0-alpha.7", path = "../rama-core" }
rama-http-types = { version = "0.2.0-alpha.7", path = "../rama-http-types" }
rama-net = { version = "0.2.0-alpha.7", path = "../rama-net", optional = true }
rama-utils = { version = "0.2.0-alpha.7", path = "../rama-utils" }
serde = { workspace = true, features = ["derive"] }
venndb = { workspace = true, optional = true }

[dev-dependencies]
serde_json = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions rama-ua/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@

mod ua;
pub use ua::*;

mod profile;
pub use profile::*;
144 changes: 144 additions & 0 deletions rama-ua/src/profile/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use rama_core::error::OpaqueError;
use rama_http_types::proto::h2::PseudoHeader;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "memory-db", derive(venndb::VennDB))]
pub struct HttpProfile {
#[cfg_attr(feature = "memory-db", venndb(key))]
pub ja4h: String,
pub http_headers: Vec<(String, String)>,
pub http_pseudo_headers: Vec<PseudoHeader>,
#[cfg_attr(feature = "memory-db", venndb(filter))]
pub fetch_mode: Option<FetchMode>,
#[cfg_attr(feature = "memory-db", venndb(filter))]
pub resource_type: Option<FetchMode>,
#[cfg_attr(feature = "memory-db", venndb(filter))]
pub initiator: Option<Initiator>,
#[cfg_attr(feature = "memory-db", venndb(filter))]
pub http_version: Option<HttpVersion>,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub enum FetchMode {
Cors,
Navigate,
NoCors,
SameOrigin,
Websocket,
}

impl std::fmt::Display for FetchMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Cors => write!(f, "cors"),
Self::Navigate => write!(f, "navigate"),
Self::NoCors => write!(f, "no-cors"),
Self::SameOrigin => write!(f, "same-origin"),
Self::Websocket => write!(f, "websocket"),
}
}
}

impl FromStr for FetchMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cors" => Ok(Self::Cors),
"navigate" => Ok(Self::Navigate),
"no-cors" => Ok(Self::NoCors),
"same-origin" => Ok(Self::SameOrigin),
"websocket" => Ok(Self::Websocket),
_ => Err(s.to_owned()),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub enum ResourceType {
Document,
Xhr,
Form,
}

impl std::fmt::Display for ResourceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Document => write!(f, "document"),
Self::Xhr => write!(f, "xhr"),
Self::Form => write!(f, "form"),
}
}
}

impl FromStr for ResourceType {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"document" => Ok(Self::Document),
"xhr" => Ok(Self::Xhr),
"form" => Ok(Self::Form),
_ => Err(s.to_owned()),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub enum Initiator {
Navigator,
Fetch,
XMLHttpRequest,
Form,
}

impl std::fmt::Display for Initiator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Navigator => write!(f, "navigator"),
Self::Fetch => write!(f, "fetch"),
Self::XMLHttpRequest => write!(f, "xmlhttprequest"),
Self::Form => write!(f, "form"),
}
}
}

impl FromStr for Initiator {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"navigator" => Ok(Self::Navigator),
"fetch" => Ok(Self::Fetch),
"xmlhttprequest" => Ok(Self::XMLHttpRequest),
"form" => Ok(Self::Form),
_ => Err(s.to_owned()),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
pub enum HttpVersion {
H1,
H2,
H3,
}

impl FromStr for HttpVersion {
type Err = OpaqueError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.trim().to_lowercase().as_str() {
"h1" | "http1" | "http/1" | "http/1.0" | "http/1.1" => Self::H1,
"h2" | "http2" | "http/2" | "http/2.0" => Self::H2,
"h3" | "http3" | "http/3" | "http/3.0" => Self::H3,
version => {
return Err(OpaqueError::from_display(format!(
"unsupported http version: {version}"
)))
}
})
}
}
10 changes: 10 additions & 0 deletions rama-ua/src/profile/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mod ua;
pub use ua::*;

mod http;
pub use http::*;

#[cfg(feature = "tls")]
mod tls;
#[cfg(feature = "tls")]
pub use tls::*;
10 changes: 10 additions & 0 deletions rama-ua/src/profile/tls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use rama_net::tls::client::ClientHello;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "memory-db", derive(venndb::VennDB))]
pub struct TlsProfile {
#[cfg_attr(feature = "memory-db", venndb(key))]
pub ja4: String,
pub client_hello: ClientHello,
}
Loading

0 comments on commit bff7d85

Please sign in to comment.