use crate::error::Error;
use std::{
io::{BufRead, BufReader, Cursor},
ops::Deref,
vec,
};
#[cfg_attr(
any(feature = "native-tls", feature = "rustls-tls"),
doc = r##"
```rust,no_run
# use elasticsearch::{
# auth::Credentials,
# cert::{Certificate,CertificateValidation},
# Error, Elasticsearch,
# http::transport::{TransportBuilder,SingleNodeConnectionPool},
# };
# use std::fs::File;
# use std::io::Read;
# use url::Url;
# async fn doc() -> Result<(), Box<dyn std::error::Error>> {
let url = Url::parse("https://example.com")?;
let conn_pool = SingleNodeConnectionPool::new(url);
// load the CA certificate
let mut buf = Vec::new();
File::open("my_ca_cert.pem")?
.read_to_end(&mut buf)?;
let cert = Certificate::from_pem(&buf)?;
let transport = TransportBuilder::new(conn_pool)
.cert_validation(CertificateValidation::Full(cert))
.build()?;
let client = Elasticsearch::new(transport);
let _response = client.ping().send().await?;
# Ok(())
# }
```
"##
)]
#[cfg_attr(
feature = "native-tls",
doc = r##"
```rust,no_run
# use elasticsearch::{
# auth::Credentials,
# cert::{Certificate,CertificateValidation},
# Error, Elasticsearch,
# http::transport::{TransportBuilder,SingleNodeConnectionPool},
# };
# use std::fs::File;
# use std::io::Read;
# use url::Url;
# async fn doc() -> Result<(), Box<dyn std::error::Error>> {
let url = Url::parse("https://example.com")?;
let conn_pool = SingleNodeConnectionPool::new(url);
// load the CA certificate
let mut buf = Vec::new();
File::open("my_ca_cert.pem")?
.read_to_end(&mut buf)?;
let cert = Certificate::from_pem(&buf)?;
let transport = TransportBuilder::new(conn_pool)
.cert_validation(CertificateValidation::Certificate(cert))
.build()?;
let client = Elasticsearch::new(transport);
let _response = client.ping().send().await?;
# Ok(())
# }
```
"##
)]
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
pub enum CertificateValidation {
Default,
Full(Certificate),
#[cfg(feature = "native-tls")]
Certificate(Certificate),
None,
}
const BEGIN_CERTIFICATE: &str = "-----BEGIN CERTIFICATE-----";
const END_CERTIFICATE: &str = "-----END CERTIFICATE-----";
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
pub struct Certificate(Vec<reqwest::Certificate>);
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
impl Certificate {
pub fn from_pem(pem: &[u8]) -> Result<Self, Error> {
let reader = BufReader::new(Cursor::new(pem));
let mut certs = Vec::new();
let mut cert = Vec::new();
let mut begin = false;
for line in reader.lines() {
let line = line?;
match line.as_ref() {
BEGIN_CERTIFICATE if !begin => {
begin = true;
cert.push(line);
}
END_CERTIFICATE if begin => {
begin = false;
cert.push(line);
certs.push(reqwest::Certificate::from_pem(cert.join("\n").as_bytes())?);
cert = Vec::new();
}
_ if begin => cert.push(line),
_ => {}
}
}
if certs.is_empty() {
Err(crate::error::lib(
"could not find PEM certificate in input data",
))
} else {
Ok(Self(certs))
}
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
Ok(Self(vec![reqwest::Certificate::from_der(der)?]))
}
pub fn append(&mut self, mut cert: Self) {
self.0.append(&mut cert.0);
}
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
impl IntoIterator for Certificate {
type Item = reqwest::Certificate;
type IntoIter = vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[cfg(any(feature = "native-tls", feature = "rustls-tls"))]
impl Deref for Certificate {
type Target = Vec<reqwest::Certificate>;
fn deref(&self) -> &Self::Target {
&self.0
}
}