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

Code before:
            curl_sys::curl_easy_getinfo(easy.raw(), curl_sys::CURLINFO_CERTINFO, &mut certinfo);
        cvt(easy, rc)?;
        if certinfo.is_null() {
            return Ok(None);
        }
        let count = (*certinfo).num_of_certs;
        if count <= 0 {
            return Ok(None);
        }
        let slist = *((*certinfo).certinfo.offset(0));
        let data = to_list(slist);
        let pem = extract_pem_from_certinfo(&data);
        
        Ok(Some(CertInfo { data, pem }))
    }
}
// Extract PEM certificate from certificate info data
fn extract_pem_from_certinfo(data: &[String]) -> Option<String> {
    let mut pem_lines = Vec::new();
    let mut in_pem = false;
    
    for line in data {
        let trimmed = line.trim();
        
        // Check if line contains PEM header
        if trimmed.contains("-----BEGIN CERTIFICATE-----") {
            in_pem = true;
            // If the line has a prefix like "Cert:", extract just the PEM part
            if let Some(pem_start) = line.find("-----BEGIN CERTIFICATE-----") {
                pem_lines.push(line[pem_start..].to_string());
            } else {
                pem_lines.push(line.clone());
            }
        } else if in_pem {
            pem_lines.push(line.clone());
            if trimmed.contains("-----END CERTIFICATE-----") {
                break;
            }
        }
    }
    
    if !pem_lines.is_empty() {
        Some(pem_lines.join("\n"))
    } else {
        None
    }
}
/// Returns the connection identifier use by this libcurl handle.
pub fn conn_id(easy: &Easy) -> Result<i64, Error> {
    unsafe {
        let conn_id: curl_off_t = 0;

Review comment (suggestion):
I prefer that we identify the value of the cert with a condition on the begin of the line. If a line is starting with `Cert:`, then the rest of the line is the certificate value, in PEM format. We don't need to remove the header and footer (`-----BEGIN CERTIFICATE-----`, `-----END CERTIFICATE-----`)

Code after:
        Self(value)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

/// Returns the information of the first certificate in the certificates chain.
pub fn cert_info(easy: &Easy) -> Result<Option<CertInfo>, Error> {
    unsafe {
        let mut certinfo = ptr::null_mut::<curl_certinfo>();
        let rc =
            curl_sys::curl_easy_getinfo(easy.raw(), curl_sys::CURLINFO_CERTINFO, &mut certinfo);
        cvt(easy, rc)?;
        if certinfo.is_null() {
            return Ok(None);
        }
        let count = (*certinfo).num_of_certs;
        if count <= 0 {
            return Ok(None);
        }
        let slist = *((*certinfo).certinfo.offset(0));
        let data = to_list(slist);
        let value = extract_pem_from_certinfo(&data);

        Ok(Some(CertInfo { data, value }))
    }
}
/// Extracts PEM certificate value from certificate info data.
fn extract_pem_from_certinfo(data: &[String]) -> Option<Pem> {
    for line in data {
        let trimmed = line.trim();
        if let Some(value) = trimmed.strip_prefix("Cert:") {
            return Some(Pem::new(value.to_string()));
        }
    }
    None
}
/// Returns the connection identifier use by this libcurl handle.
pub fn conn_id(easy: &Easy) -> Result<i64, Error> {
    unsafe {
        let conn_id: curl_off_t = 0;
        let rc = curl_sys::curl_easy_getinfo(easy.raw(), CURLINFO_CONN_ID, &conn_id);
        cvt(easy, rc)?;
        Ok(conn_id)
    }
}

// Timing of a typical HTTP exchange (over TLS 1.2 connection) from libcurl
// (courtesy of <https://blog.cloudflare.com/a-question-of-timing/>