use std::time::Duration;
pub(crate) const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
pub(crate) const AUTH_HEADER_NAME: &str = "X-BUGZILLA-API-KEY";
pub(crate) const AUTH_QUERY_PARAM: &str = "Bugzilla_api_key";
pub(crate) fn apply_auth_to_request(
builder: reqwest::RequestBuilder,
header: Option<&reqwest::header::HeaderValue>,
query_key: Option<&str>,
) -> reqwest::RequestBuilder {
if let Some(val) = header {
builder.header(AUTH_HEADER_NAME, val.clone())
} else if let Some(key) = query_key {
builder.query(&[(AUTH_QUERY_PARAM, key)])
} else {
builder
}
}
pub(crate) fn apply_auth(
builder: reqwest::RequestBuilder,
api_key: &str,
method: crate::types::AuthMethod,
) -> crate::error::Result<reqwest::RequestBuilder> {
match method {
crate::types::AuthMethod::Header => {
let val = reqwest::header::HeaderValue::from_str(api_key).map_err(|_| {
crate::error::BzrError::config("API key contains invalid header characters")
})?;
Ok(apply_auth_to_request(builder, Some(&val), None))
}
crate::types::AuthMethod::QueryParam => {
Ok(apply_auth_to_request(builder, None, Some(api_key)))
}
}
}
pub(crate) fn looks_like_tls_error(msg: &str) -> bool {
let lower = msg.to_ascii_lowercase();
lower.contains("cert") || lower.contains("ssl") || lower.contains("tls")
}
pub(crate) fn is_connect_tls_error(is_connect: bool, error_chain: &str) -> bool {
is_connect && looks_like_tls_error(error_chain)
}
pub(crate) fn is_tls_cert_error(err: &reqwest::Error) -> bool {
is_connect_tls_error(err.is_connect(), &crate::error::format_error_chain(err))
}
pub(crate) const TLS_HINT: &str =
"\n hint: to trust this server's certificate, re-run interactively,\n \
or pre-pin with: bzr config set-server <NAME> --tls-pin-now\n \
or provide a CA: bzr config set-server <NAME> --tls-ca-cert <PATH>\n \
or skip verification: bzr config set-server <NAME> --tls-insecure";
pub(crate) fn tls_hint(base_msg: &str, err: &reqwest::Error) -> String {
let mut msg = base_msg.to_string();
if is_tls_cert_error(err) {
msg.push_str(TLS_HINT);
}
msg
}
pub(crate) fn redact_api_key(msg: &str) -> String {
let marker = format!("{AUTH_QUERY_PARAM}=");
if let Some(idx) = msg.find(&marker) {
let prefix = &msg[..idx + marker.len()];
let rest = &msg[idx + marker.len()..];
let end = rest.find(['&', ')', ' ']).unwrap_or(rest.len());
format!("{prefix}[REDACTED]{}", &rest[end..])
} else {
msg.to_string()
}
}
#[cfg(test)]
#[path = "http_tests.rs"]
mod tests;