autopush_common/util/
user_agent.rs

1use woothee::parser::Parser;
2
3// List of valid user-agent attributes to keep, anything not in this
4// list is considered 'Other'. We log the user-agent on connect always
5// to retain the full string, but for DD more tags are expensive so we
6// limit to these.
7const VALID_UA_BROWSER: &[&str] = &["Chrome", "Firefox", "Safari", "Opera"];
8
9// See dataset.rs in https://github.com/woothee/woothee-rust for the
10// full list (WootheeResult's 'os' field may fall back to its 'name'
11// field). Windows has many values and we only care that its Windows
12const VALID_UA_OS: &[&str] = &["Firefox OS", "Linux", "Mac OSX"];
13
14#[derive(Clone, Debug, Default)]
15pub struct UserAgentInfo {
16    _user_agent_string: String,
17    pub category: String,
18    pub browser_name: String,
19    pub browser_version: String,
20    pub metrics_browser: String,
21    pub metrics_os: String,
22    pub os_version: String,
23    pub os: String,
24    // Note, Woothee can determine if a user agent is mobile if the
25    // ["smartphone", "mobilephone"].contains(category)
26}
27
28impl From<&str> for UserAgentInfo {
29    fn from(user_agent_string: &str) -> Self {
30        let parser = Parser::new();
31        let wresult = parser.parse(user_agent_string).unwrap_or_default();
32
33        // Determine a base os/browser for metrics' tags
34        let metrics_os = if wresult.os.starts_with("Windows") {
35            "Windows"
36        } else if VALID_UA_OS.contains(&wresult.os) {
37            wresult.os
38        } else {
39            "Other"
40        };
41        let metrics_browser = if VALID_UA_BROWSER.contains(&wresult.name) {
42            wresult.name
43        } else {
44            "Other"
45        };
46
47        Self {
48            category: wresult.category.to_owned(),
49            browser_name: wresult.name.to_owned(),
50            browser_version: wresult.version.to_owned(),
51            metrics_browser: metrics_browser.to_owned(),
52            metrics_os: metrics_os.to_owned(),
53            os_version: wresult.os_version.to_string(),
54            os: wresult.os.to_owned(),
55            _user_agent_string: user_agent_string.to_owned(),
56        }
57    }
58}
59
60impl From<&actix_web::HttpRequest> for UserAgentInfo {
61    fn from(req: &actix_web::HttpRequest) -> UserAgentInfo {
62        if let Some(header) = req.headers().get(&actix_web::http::header::USER_AGENT) {
63            Self::from(header.to_str().unwrap_or("UNKNOWN"))
64        } else {
65            UserAgentInfo::default()
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::UserAgentInfo;
73
74    #[test]
75    fn test_linux() {
76        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)"#;
77        let ua_result = UserAgentInfo::from(agent);
78        assert_eq!(ua_result.metrics_os, "Linux");
79        assert_eq!(ua_result.os, "Linux");
80        assert_eq!(ua_result.metrics_browser, "Firefox");
81    }
82
83    #[test]
84    fn test_windows() {
85        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)"#;
86        let ua_result = UserAgentInfo::from(agent);
87        assert_eq!(ua_result.metrics_os, "Windows");
88        assert_eq!(ua_result.os, "Windows 7");
89        assert_eq!(ua_result.metrics_browser, "Firefox");
90    }
91
92    #[test]
93    fn test_osx() {
94        let agent =
95            r#"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.5; rv:2.1.1) Gecko/ Firefox/5.0.1"#;
96        let ua_result = UserAgentInfo::from(agent);
97        assert_eq!(ua_result.metrics_os, "Mac OSX");
98        assert_eq!(ua_result.os, "Mac OSX");
99        assert_eq!(ua_result.metrics_browser, "Firefox");
100    }
101
102    #[test]
103    fn test_other() {
104        let agent =
105            r#"BlackBerry9000/4.6.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102"#;
106        let ua_result = UserAgentInfo::from(agent);
107        assert_eq!(ua_result.metrics_os, "Other");
108        assert_eq!(ua_result.os, "BlackBerry");
109        assert_eq!(ua_result.metrics_browser, "Other");
110        assert_eq!(ua_result.browser_name, "UNKNOWN");
111    }
112}