Add example in how to use this with a https clickhouse URL
It seems to need a few moving parts to connect this lib with an HTTPS clickhouse URL and probably a lot of people would like to see an example of which they can copy in and replace their parameters without having to find it out based on other peoples code. Would be pretty useful example IMHO
@joshstevens19, do you need certificates or just an example of using it with an HTTPS URL with enabled TLS feature?
If it is just HTTPS, you could use this example: https://github.com/ClickHouse/clickhouse-rs/blob/master/examples/clickhouse_cloud.rs, which should be sufficient, as CH Cloud is HTTPS.
It is now also mentioned in the website docs: https://clickhouse.com/docs/en/integrations/rust#https-or-clickhouse-cloud-connection
Feel free to re-open if you still have questions.
Is there an example for connecting to https with self-signed certificate?
+1, need this
+1, need this
Got my answer: https://github.com/ClickHouse/clickhouse-rs/issues/164
+1, need this
Got my answer: #164
Could you please provide an example?
The short answer was that it was not possible because it is a bad practice. The work around was too complicated and I couldn’t get it to work. So for POC i used http.
This issue is about examples of connecting to HTTPS, not about providing self-signed certificates directly to the crate.
If such functionality is highly desired, feel free to create a dedicated issue (#164 was about the impossibility of using self-signed certificates while it's possible, not about a dedicated API to provide such certificates directly to the crate). The implementation will look like https://docs.rs/reqwest/latest/reqwest/tls/struct.Certificate.html, so feel free to describe whether it's enough.
I did it; it was quite challenging, but now it's functioning properly with Yandex Cloud.
// clickhouse-client.rs
use clickhouse::Client;
use hyper_rustls::HttpsConnectorBuilder;
use hyper_util::client::legacy::Client as HyperClient;
use hyper_util::rt::TokioExecutor;
use rustls::pki_types::CertificateDer;
use rustls::{ClientConfig, RootCertStore};
use rustls_pemfile::Item;
use std::fs;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ClickHouseError {
#[error("ClickHouse error: {0}")]
ConnectionError(#[from] clickhouse::error::Error),
#[error("Rustls error: {0}")]
RustlsError(#[from] rustls::Error),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
}
#[derive(Debug, Clone)]
pub struct ClickHouseConfig {
pub url: String,
pub username: String,
pub password: String,
pub database: Option<String>,
pub ca_cert_path: Option<String>,
}
pub struct ClickHouseClient {
client: Client,
}
impl ClickHouseClient {
pub fn new(config: ClickHouseConfig) -> Result<Self, ClickHouseError> {
let https = if let Some(cert_path) = &config.ca_cert_path {
let cert = fs::read(cert_path)?;
let mut cert_reader = std::io::BufReader::new(cert.as_slice());
let cert = match rustls_pemfile::read_one(&mut cert_reader)? {
Some(Item::X509Certificate(cert_der)) => CertificateDer::from(cert_der),
_ => {
return Err(ClickHouseError::IoError(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid certificate format",
)))
}
};
let mut root_store = RootCertStore::empty();
root_store.add(cert)?;
let tls_config =
ClientConfig::builder().with_root_certificates(root_store).with_no_client_auth();
HttpsConnectorBuilder::new()
.with_tls_config(tls_config)
.https_only()
.enable_http1()
.build()
} else {
HttpsConnectorBuilder::new()
.with_tls_config(
ClientConfig::builder()
.with_root_certificates(RootCertStore::empty())
.with_no_client_auth(),
)
.https_or_http()
.enable_http1()
.build()
};
let hyper_client = HyperClient::builder(TokioExecutor::new()).build(https);
let mut client = Client::with_http_client(hyper_client)
.with_url(config.url)
.with_user(config.username)
.with_password(config.password);
if let Some(database) = config.database {
client = client.with_database(database);
}
Ok(Self { client })
}
pub fn get_client(&self) -> &Client {
&self.client
}
}
// Cargo.toml
[package]
name = "utils"
version = "0.1.0"
edition = "2021"
[dependencies]
clickhouse-derive = { version = "0.2.0" }
clickhouse = "0.13.1"
rustls = "0.23"
hyper-util = { version = "0.1.6", features = ["client-legacy", "http1"] }
hyper-rustls = { version = "0.27.3", default-features = false, features = [ "http1", "tls12" ] }
rustls-pemfile = "2.2"
native-tls = "0.2.11"
I needed to synchronize the versions of various packages to get this to work.
I hope this helps someone.
This is the only working example I could find. Thy you for sharing @ifokeev