Read the mTLS cert + key PEM files and assemble a `reqwest::Identity`. `reqwest::Identity::from_pem` (with the `rustls-tls` backend) wants a single PEM blob containing the certificate chain followed by the private key. We concatenate the two files with a newline separator — stray trailing newlines in either file are tolerated by the PEM parser. Errors at every step (file I/O, PEM parsing) are map
(
cert_path: &std::path::Path,
key_path: &std::path::Path,
)
| 683 | /// `anyhow` with the source path included so misconfigurations surface |
| 684 | /// clearly. |
| 685 | fn load_mtls_identity( |
| 686 | cert_path: &std::path::Path, |
| 687 | key_path: &std::path::Path, |
| 688 | ) -> Result<reqwest::Identity> { |
| 689 | let cert = std::fs::read(cert_path).map_err(|e| { |
| 690 | anyhow!( |
| 691 | "failed to read mTLS client_cert_pem at {}: {}", |
| 692 | cert_path.display(), |
| 693 | e |
| 694 | ) |
| 695 | })?; |
| 696 | let key = std::fs::read(key_path).map_err(|e| { |
| 697 | anyhow!( |
| 698 | "failed to read mTLS client_key_pem at {}: {}", |
| 699 | key_path.display(), |
| 700 | e |
| 701 | ) |
| 702 | })?; |
| 703 | |
| 704 | let mut pem = Vec::with_capacity(cert.len() + key.len() + 1); |
| 705 | pem.extend_from_slice(&cert); |
| 706 | if !cert.ends_with(b"\n") { |
| 707 | pem.push(b'\n'); |
| 708 | } |
| 709 | pem.extend_from_slice(&key); |
| 710 | |
| 711 | reqwest::Identity::from_pem(&pem).map_err(|e| { |
| 712 | anyhow!( |
| 713 | "failed to parse mTLS PEM material (cert={}, key={}): {}", |
| 714 | cert_path.display(), |
| 715 | key_path.display(), |
| 716 | e |
| 717 | ) |
| 718 | }) |
| 719 | } |
| 720 | |
| 721 | /// Truncate `s` to at most `max_bytes`, rounding down to the nearest UTF-8 |
| 722 | /// character boundary to keep the result a valid `&str`. |