autopush_common/util/
user_agent.rs1use woothee::parser::Parser;
2
3const VALID_UA_BROWSER: &[&str] = &["Chrome", "Firefox", "Safari", "Opera"];
8
9const VALID_UA_OS: &[&str] = &[
18 "Firefox OS",
19 "Linux",
20 "Mac OSX",
21 "Android",
22 "iPhone",
23 "iPad",
24 "iPod",
25];
26
27const UA_METRIC_UNKNOWN: &str = "Other";
29
30#[derive(Debug)]
31pub struct UserAgentInfo {
32 _user_agent_string: String,
33 pub category: String,
34 pub browser_name: String,
35 pub browser_version: String,
36 pub metrics_browser: String,
37 pub metrics_os: String,
38 pub os_version: String,
39 pub os: String,
40 }
43
44impl Default for UserAgentInfo {
45 fn default() -> Self {
52 Self {
53 _user_agent_string: String::new(),
54 category: String::new(),
55 browser_name: String::new(),
56 browser_version: String::new(),
57 metrics_browser: UA_METRIC_UNKNOWN.to_owned(),
58 metrics_os: UA_METRIC_UNKNOWN.to_owned(),
59 os_version: String::new(),
60 os: String::new(),
61 }
62 }
63}
64
65impl From<&str> for UserAgentInfo {
66 fn from(user_agent_string: &str) -> Self {
67 let parser = Parser::new();
68 let wresult = parser.parse(user_agent_string).unwrap_or_default();
69
70 let metrics_os = if wresult.os.starts_with("Windows") {
72 "Windows"
73 } else if VALID_UA_OS.contains(&wresult.os) {
74 wresult.os
75 } else {
76 UA_METRIC_UNKNOWN
77 };
78 let metrics_browser = if VALID_UA_BROWSER.contains(&wresult.name) {
79 wresult.name
80 } else {
81 UA_METRIC_UNKNOWN
82 };
83
84 Self {
85 category: wresult.category.to_owned(),
86 browser_name: wresult.name.to_owned(),
87 browser_version: wresult.version.to_owned(),
88 metrics_browser: metrics_browser.to_owned(),
89 metrics_os: metrics_os.to_owned(),
90 os_version: wresult.os_version.to_string(),
91 os: wresult.os.to_owned(),
92 _user_agent_string: user_agent_string.to_owned(),
93 }
94 }
95}
96
97impl From<&actix_web::HttpRequest> for UserAgentInfo {
98 fn from(req: &actix_web::HttpRequest) -> UserAgentInfo {
99 if let Some(header) = req.headers().get(&actix_web::http::header::USER_AGENT) {
100 Self::from(header.to_str().unwrap_or("UNKNOWN"))
101 } else {
102 UserAgentInfo::default()
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::UserAgentInfo;
110
111 #[test]
112 fn test_linux() {
113 let agent = r#"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090807 Mandriva Linux/1.9.1.2-1.1mud2009.1 (2009.1) Firefox/3.5.2 FirePHP/0.3,gzip(gfe),gzip(gfe)"#;
114 let ua_result = UserAgentInfo::from(agent);
115 assert_eq!(ua_result.metrics_os, "Linux");
116 assert_eq!(ua_result.os, "Linux");
117 assert_eq!(ua_result.metrics_browser, "Firefox");
118 }
119
120 #[test]
121 fn test_windows() {
122 let agent = r#"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)"#;
123 let ua_result = UserAgentInfo::from(agent);
124 assert_eq!(ua_result.metrics_os, "Windows");
125 assert_eq!(ua_result.os, "Windows 7");
126 assert_eq!(ua_result.metrics_browser, "Firefox");
127 }
128
129 #[test]
130 fn test_osx() {
131 let agent =
132 r#"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.5; rv:2.1.1) Gecko/ Firefox/5.0.1"#;
133 let ua_result = UserAgentInfo::from(agent);
134 assert_eq!(ua_result.metrics_os, "Mac OSX");
135 assert_eq!(ua_result.os, "Mac OSX");
136 assert_eq!(ua_result.metrics_browser, "Firefox");
137 }
138
139 #[test]
142 fn test_firefox_ios() {
143 let agent = r#"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/127.0 Mobile/15E148 Safari/605.1.15"#;
144 let ua_result = UserAgentInfo::from(agent);
145 assert_eq!(ua_result.metrics_os, "iPhone");
146 assert_eq!(ua_result.os, "iPhone");
147 assert_eq!(ua_result.metrics_browser, "Firefox");
150
151 let ipad = r#"Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/127.0 Mobile/15E148 Safari/605.1.15"#;
152 assert_eq!(UserAgentInfo::from(ipad).metrics_os, "iPad");
153 }
154
155 #[test]
156 fn test_firefox_android() {
157 let agent = r#"Mozilla/5.0 (Android 14; Mobile; rv:127.0) Gecko/127.0 Firefox/127.0"#;
158 let ua_result = UserAgentInfo::from(agent);
159 assert_eq!(ua_result.metrics_os, "Android");
160 assert_eq!(ua_result.os, "Android");
161 assert_eq!(ua_result.metrics_browser, "Firefox");
162 }
163
164 #[test]
167 fn test_missing_user_agent() {
168 let ua_result = UserAgentInfo::default();
169 assert_eq!(ua_result.metrics_os, "Other");
170 assert_eq!(ua_result.metrics_browser, "Other");
171 assert_eq!(ua_result.os, "");
174 assert_eq!(ua_result.browser_name, "");
175 }
176
177 #[test]
178 fn test_other() {
179 let agent =
180 r#"BlackBerry9000/4.6.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102"#;
181 let ua_result = UserAgentInfo::from(agent);
182 assert_eq!(ua_result.metrics_os, "Other");
183 assert_eq!(ua_result.os, "BlackBerry");
184 assert_eq!(ua_result.metrics_browser, "Other");
185 assert_eq!(ua_result.browser_name, "UNKNOWN");
186 }
187}