From ab2bb06f022690879f7f2c7d0631c022d0a50bdb Mon Sep 17 00:00:00 2001 From: Dylan Johnson Date: Wed, 31 Jul 2024 11:56:57 -0400 Subject: [PATCH 1/3] add `GLEAM_CACERTS_PATH` env variable --- compiler-cli/src/http.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler-cli/src/http.rs b/compiler-cli/src/http.rs index a759fbc68e8..719d9913cfe 100644 --- a/compiler-cli/src/http.rs +++ b/compiler-cli/src/http.rs @@ -6,6 +6,7 @@ use gleam_core::{Error, Result}; use http::{Request, Response}; static REQWEST_CLIENT: OnceLock = OnceLock::new(); +static CERTS_ENV_VAR: &str = "GLEAM_CACERTS_PATH"; #[derive(Debug)] pub struct HttpClient; @@ -27,7 +28,7 @@ impl gleam_core::io::HttpClient for HttpClient { .try_into() .expect("Unable to convert HTTP request for use by reqwest library"); let mut response = REQWEST_CLIENT - .get_or_init(reqwest::Client::new) + .get_or_init(init_client) .execute(request) .await .map_err(Error::http)?; @@ -42,3 +43,32 @@ impl gleam_core::io::HttpClient for HttpClient { .map_err(Error::http) } } + +fn init_client() -> reqwest::Client { + if let Some(cert) = get_certificate() { + return reqwest::Client::builder() + .add_root_certificate(cert) + .build() + .expect("Unable to initialize a reqwest HTTP client"); + } else { + return reqwest::Client::new(); + } +} + +fn get_certificate() -> Option { + match std::env::var(CERTS_ENV_VAR) { + Ok(certs_path) => { + let data = std::fs::read(certs_path).expect(&format!( + "Unable to read certs file set as `{}`", + CERTS_ENV_VAR + )); + let cert = reqwest::Certificate::from_pem(&data).expect(&format!( + "Unable to construct a certificate from certs file set as `{}`", + CERTS_ENV_VAR + )); + + Some(cert) + } + _ => None, + } +} From 3e993c03e2836e1dfa9f1d67b31a17edd4400eab Mon Sep 17 00:00:00 2001 From: winstxnhdw Date: Mon, 10 Feb 2025 01:59:44 +0000 Subject: [PATCH 2/3] feat: use `FileIoAction` --- compiler-cli/src/http.rs | 70 ++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/compiler-cli/src/http.rs b/compiler-cli/src/http.rs index 719d9913cfe..c65b5d6520e 100644 --- a/compiler-cli/src/http.rs +++ b/compiler-cli/src/http.rs @@ -2,11 +2,15 @@ use std::convert::TryInto; use std::sync::OnceLock; use async_trait::async_trait; -use gleam_core::{Error, Result}; +use camino::Utf8PathBuf; +use gleam_core::{ + error::{FileIoAction, FileKind}, + Error, Result, +}; use http::{Request, Response}; +use reqwest::{Certificate, Client}; -static REQWEST_CLIENT: OnceLock = OnceLock::new(); -static CERTS_ENV_VAR: &str = "GLEAM_CACERTS_PATH"; +static REQWEST_CLIENT: OnceLock = OnceLock::new(); #[derive(Debug)] pub struct HttpClient; @@ -27,11 +31,8 @@ impl gleam_core::io::HttpClient for HttpClient { let request = request .try_into() .expect("Unable to convert HTTP request for use by reqwest library"); - let mut response = REQWEST_CLIENT - .get_or_init(init_client) - .execute(request) - .await - .map_err(Error::http)?; + let client = init_client().map_err(Error::http)?; + let mut response = client.execute(request).await.map_err(Error::http)?; let mut builder = Response::builder() .status(response.status()) .version(response.version()); @@ -44,31 +45,36 @@ impl gleam_core::io::HttpClient for HttpClient { } } -fn init_client() -> reqwest::Client { - if let Some(cert) = get_certificate() { - return reqwest::Client::builder() - .add_root_certificate(cert) - .build() - .expect("Unable to initialize a reqwest HTTP client"); - } else { - return reqwest::Client::new(); +fn init_client() -> Result<&'static Client, Error> { + if let Some(client) = REQWEST_CLIENT.get() { + return Ok(client); } -} -fn get_certificate() -> Option { - match std::env::var(CERTS_ENV_VAR) { - Ok(certs_path) => { - let data = std::fs::read(certs_path).expect(&format!( - "Unable to read certs file set as `{}`", - CERTS_ENV_VAR - )); - let cert = reqwest::Certificate::from_pem(&data).expect(&format!( - "Unable to construct a certificate from certs file set as `{}`", - CERTS_ENV_VAR - )); + let certificate_path = std::env::var("GLEAM_CACERTS_PATH").map_err(|error| Error::FileIo { + kind: FileKind::Directory, + action: FileIoAction::Read, + path: Utf8PathBuf::new(), + err: Some(error.to_string()), + })?; - Some(cert) - } - _ => None, - } + let certificate_bytes = std::fs::read(&certificate_path).map_err(|error| Error::FileIo { + kind: FileKind::File, + action: FileIoAction::Parse, + path: Utf8PathBuf::from(&certificate_path), + err: Some(error.to_string()), + })?; + + let certificate = Certificate::from_pem(&certificate_bytes).map_err(|error| Error::FileIo { + kind: FileKind::File, + action: FileIoAction::Parse, + path: Utf8PathBuf::from(&certificate_path), + err: Some(error.to_string()), + })?; + + Ok(REQWEST_CLIENT.get_or_init(|| { + Client::builder() + .add_root_certificate(certificate) + .build() + .expect("Failed to create reqwest client") + })) } From 44b88262649a3a7a67e2fc35c7f80cebab9e73a2 Mon Sep 17 00:00:00 2001 From: winstxnhdw Date: Tue, 11 Feb 2025 00:26:33 +0000 Subject: [PATCH 3/3] refactor: build default client if `CACERTS_PATH` is not populated --- CHANGELOG.md | 4 ++++ compiler-cli/src/http.rs | 26 +++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c946f091ce..dd144f1ad8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ making Gleam packages discoverable through global search of HexDocs. ([Diemo Gebhardt](https://github.com/diemogebhardt)) +- Allow users to set the `GLEAM_CACERTS_PATH` environment variable to specify a + path to a directory containing CA certificates to install Hex packages. + ([winstxnhdw](https://github.com/winstxnhdw)) + ### Language server ### Formatter diff --git a/compiler-cli/src/http.rs b/compiler-cli/src/http.rs index c65b5d6520e..5df62d1a68f 100644 --- a/compiler-cli/src/http.rs +++ b/compiler-cli/src/http.rs @@ -10,6 +10,8 @@ use gleam_core::{ use http::{Request, Response}; use reqwest::{Certificate, Client}; +use crate::fs; + static REQWEST_CLIENT: OnceLock = OnceLock::new(); #[derive(Debug)] @@ -50,20 +52,18 @@ fn init_client() -> Result<&'static Client, Error> { return Ok(client); } - let certificate_path = std::env::var("GLEAM_CACERTS_PATH").map_err(|error| Error::FileIo { - kind: FileKind::Directory, - action: FileIoAction::Read, - path: Utf8PathBuf::new(), - err: Some(error.to_string()), - })?; - - let certificate_bytes = std::fs::read(&certificate_path).map_err(|error| Error::FileIo { - kind: FileKind::File, - action: FileIoAction::Parse, - path: Utf8PathBuf::from(&certificate_path), - err: Some(error.to_string()), - })?; + let certificate_path = match std::env::var("GLEAM_CACERTS_PATH") { + Ok(path) => path, + Err(_) => { + return Ok(REQWEST_CLIENT.get_or_init(|| { + Client::builder() + .build() + .expect("Failed to create reqwest client") + })); + } + }; + let certificate_bytes = fs::read_bytes(&certificate_path)?; let certificate = Certificate::from_pem(&certificate_bytes).map_err(|error| Error::FileIo { kind: FileKind::File, action: FileIoAction::Parse,