Repository: Orange-OpenSource/hurl
File: packages/hurl/src/http/client.rs (Rust)

Code before:
        // When Hurl explicitly sets `Expect:` to remove the header, libcurl will generate
        // `SignedHeaders` that include `expect` even though the header is not present, causing
        // some APIs to reject the request.
        // Therefore, we only remove this header when not in aws_sigv4 mode.
        if !headers.contains_key(EXPECT) && options.aws_sigv4.is_none() {
            // We remove default Expect headers added by curl because we want to explicitly manage
            // this header.
            list.append(&format!("{EXPECT}:"))?;
        }

        if !headers.contains_key(USER_AGENT) {
            let user_agent = match options.user_agent {
                Some(ref u) => u.clone(),
                None => {
                    let pkg_version = env!("CARGO_PKG_VERSION");
                    format!("hurl/{pkg_version}")
                }
            };
            list.append(&format!("{USER_AGENT}: {user_agent}"))?;
        }

        if let Some(user) = &options.user {
            if options.aws_sigv4.is_some() || options.ntlm || options.negotiate {
                // curl's aws_sigv4 support needs to know the username and password for the
                // request, as it uses those values to calculate the Authorization header for the
                // AWS V4 signature.
                //
                // --negotiate also requires a username and password, though they are not used:
                // > When using this option, you must also provide a fake `-u, --user` option to
                // > activate the authentication code properly. Sending a '-u :' is enough, as the
                // > username and password from the `-u, --user` option are not actually used.
                if let Some((username, password)) = user.split_once(':') {
                    self.handle.username(username)?;
                    self.handle.password(password)?;
                }
            } else {
                let user = user.as_bytes();
                let authorization = general_purpose::STANDARD.encode(user);
                if !headers.contains_key(AUTHORIZATION) {
                    list.append(&format!("{AUTHORIZATION}: Basic {authorization}"))?;
                }
            }
        }
        if options.compressed && !headers.contains_key(ACCEPT_ENCODING) {
            list.append(&format!("{ACCEPT_ENCODING}: gzip, deflate, br"))?;
        }

        self.handle.http_headers(list)?;
        Ok(())
    }


Review comment (suggestion):
@ashishajr I've missed this line in the `--ntlm` PR, would you be kind to make a PR updating the comment and explaining why we take `options.ntlm` into account here? The comment is only refererring to `aws_sigv4` I don't see the link with `ntlm`

Code after:
        }

        // Workaround for libcurl issue <https://github.com/curl/curl/issues/11664>:
        // When Hurl explicitly sets `Expect:` to remove the header, libcurl will generate
        // `SignedHeaders` that include `expect` even though the header is not present, causing
        // some APIs to reject the request.
        // Therefore, we only remove this header when not in aws_sigv4 mode.
        if !headers.contains_key(EXPECT) && options.aws_sigv4.is_none() {
            // We remove default Expect headers added by curl because we want to explicitly manage
            // this header.
            list.append(&format!("{EXPECT}:"))?;
        }

        if !headers.contains_key(USER_AGENT) {
            let user_agent = match options.user_agent {
                Some(ref u) => u.clone(),
                None => {
                    let pkg_version = env!("CARGO_PKG_VERSION");
                    format!("hurl/{pkg_version}")
                }
            };
            list.append(&format!("{USER_AGENT}: {user_agent}"))?;
        }

        if let Some(user) = &options.user {
            if options.aws_sigv4.is_some() || options.ntlm || options.negotiate {
                // curl's aws_sigv4 support needs to know the username and password for the
                // request, as it uses those values to calculate the Authorization header for the
                // AWS V4 signature.
                //
                // --ntlm requires a username and password to be provided in order to complete the
                // authentication process. With curl, this would be `-u username:password`
                //
                // --negotiate requires a username and password, though they are not used.
                // From the curl man page:
                // > When using this option, you must also provide a fake `-u, --user` option to
                // > activate the authentication code properly. Sending a '-u :' is enough, as the
                // > username and password from the `-u, --user` option are not actually used.
                if let Some((username, password)) = user.split_once(':') {
                    self.handle.username(username)?;
                    self.handle.password(password)?;
                }
            } else {
                let user = user.as_bytes();
                let authorization = general_purpose::STANDARD.encode(user);
                if !headers.contains_key(AUTHORIZATION) {
                    list.append(&format!("{AUTHORIZATION}: Basic {authorization}"))?;
                }
            }
        }
        if options.compressed && !headers.contains_key(ACCEPT_ENCODING) {