Repository: actix/actix-web
File: src/info.rs (Rust)

Code before:
        let mut realip_remote_addr = None;

        for el in req
            .headers
            .get_all(&header::FORWARDED)
            .into_iter()
            .filter_map(|hdr| hdr.to_str().ok())
            .flat_map(|val| val.split(';'))
            .flat_map(|pair| pair.split(','))
        {
            let mut items = el.trim().splitn(2, '=');
            if let (Some(name), Some(val)) = (items.next(), items.next()) {
                match name.to_lowercase().as_ref() {
                    "for" => {
                        realip_remote_addr.get_or_insert_with(|| val.trim());
                    }
                    "proto" => {
                        scheme.get_or_insert_with(|| val.trim());
                    }
                    "host" => {
                        host.get_or_insert_with(|| val.trim());
                    }
                    _ => {}
                }
            }
        }

        let first_header_value = |name| {
            let val = req
                .headers
                .get(name)?
                .to_str()
                .ok()?
                .split(',')
                .next()?
                .trim();
            Some(val)
        };

        let scheme = scheme
            .or_else(|| first_header_value(X_FORWARDED_PROTO))
            .or_else(|| req.uri.scheme().map(Scheme::as_str))
            .or_else(|| Some("https").filter(|_| cfg.secure()))
            .unwrap_or("http")
            .to_owned();

        let host = host
            .or_else(|| first_header_value(X_FORWARDED_HOST))
            .or_else(|| req.headers.get(&header::HOST)?.to_str().ok())
            .or_else(|| req.uri.authority().map(Authority::as_str))
            .unwrap_or(cfg.host())

Review comment (suggestion):
```suggestion
        req
            .headers
            .get_all(&header::FORWARDED)
            .into_iter()
            .filter_map(|hdr| hdr.to_str().ok())
            .flat_map(|val| val.split(';'))
            .flat_map(|pair| pair.split(','))
            .filter_map(|pair|
                let mut items = pair.trim().splitn(2, '=');
                Some((items.next()?, items.next()?))
            })
            .for_each(|(name, val)| {
                match name.to_lowercase().as_ref() {
                    "for" => &mut realip_remote_addr,
                    "proto" => &mut scheme,
                    "host" => &mut host,
                    _ => &mut Some(""),
                }
                .get_or_insert_with(|| val.trim());
            });
```

Code after:
/// strings instead of IP addresses or other types to acknowledge that they may be
/// [obfuscated][rfc7239-63] or [unknown][rfc7239-62].
///
/// If the older, related headers are also present (eg. `X-Forwarded-For`), then `Forwarded`
/// is preferred.
///
/// [rfc7239]: https://datatracker.ietf.org/doc/html/rfc7239
/// [rfc7239-62]: https://datatracker.ietf.org/doc/html/rfc7239#section-6.2
/// [rfc7239-63]: https://datatracker.ietf.org/doc/html/rfc7239#section-6.3
#[derive(Debug, Clone, Default)]
pub struct ConnectionInfo {
    scheme: String,
    host: String,
    realip_remote_addr: Option<String>,
    remote_addr: Option<String>,
}

impl ConnectionInfo {
    /// Create *ConnectionInfo* instance for a request.
    pub fn get<'a>(req: &'a RequestHead, cfg: &AppConfig) -> Ref<'a, Self> {
        if !req.extensions().contains::<ConnectionInfo>() {
            req.extensions_mut().insert(ConnectionInfo::new(req, cfg));
        }
        Ref::map(req.extensions(), |e| e.get().unwrap())
    }

    fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo {
        let mut host = None;
        let mut scheme = None;
        let mut realip_remote_addr = None;

        for (name, val) in req
            .headers
            .get_all(&header::FORWARDED)
            .into_iter()
            .filter_map(|hdr| hdr.to_str().ok())
            // "for=1.2.3.4, for=5.6.7.8; scheme=https"
            .flat_map(|val| val.split(';'))
            // ["for=1.2.3.4, for=5.6.7.8", " scheme=https"]
            .flat_map(|vals| vals.split(','))
            // ["for=1.2.3.4", " for=5.6.7.8", " scheme=https"]
            .flat_map(|pair| {
                let mut items = pair.trim().splitn(2, '=');
                Some((items.next()?, items.next()?))
            })
        {
            // [(name , val      ), ...                                    ]
            // [("for", "1.2.3.4"), ("for", "5.6.7.8"), ("scheme", "https")]

            // taking the first value for each property is correct because spec states that first
            // "for" value is client and rest are proxies; multiple values other properties have