⭐️ A friendly language for building type-safe, scalable systems!
at main 2.3 kB view raw
1use std::convert::TryInto; 2use std::sync::OnceLock; 3 4use async_trait::async_trait; 5use camino::Utf8PathBuf; 6use gleam_core::{ 7 Error, Result, 8 error::{FileIoAction, FileKind}, 9}; 10use http::{Request, Response}; 11use reqwest::{Certificate, Client}; 12 13use crate::fs; 14 15static REQWEST_CLIENT: OnceLock<Client> = OnceLock::new(); 16 17#[derive(Debug)] 18pub struct HttpClient; 19 20impl HttpClient { 21 pub fn new() -> Self { 22 Self 23 } 24 25 pub fn boxed() -> Box<Self> { 26 Box::new(Self::new()) 27 } 28} 29 30#[async_trait] 31impl gleam_core::io::HttpClient for HttpClient { 32 async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Vec<u8>>> { 33 let request = request 34 .try_into() 35 .expect("Unable to convert HTTP request for use by reqwest library"); 36 let client = init_client().map_err(Error::http)?; 37 let mut response = client.execute(request).await.map_err(Error::http)?; 38 let mut builder = Response::builder() 39 .status(response.status()) 40 .version(response.version()); 41 if let Some(headers) = builder.headers_mut() { 42 std::mem::swap(headers, response.headers_mut()); 43 } 44 builder 45 .body(response.bytes().await.map_err(Error::http)?.to_vec()) 46 .map_err(Error::http) 47 } 48} 49 50fn init_client() -> Result<&'static Client, Error> { 51 if let Some(client) = REQWEST_CLIENT.get() { 52 return Ok(client); 53 } 54 55 let certificate_path = match std::env::var("GLEAM_CACERTS_PATH") { 56 Ok(path) => path, 57 Err(_) => { 58 return Ok(REQWEST_CLIENT.get_or_init(|| { 59 Client::builder() 60 .build() 61 .expect("Failed to create reqwest client") 62 })); 63 } 64 }; 65 66 let certificate_bytes = fs::read_bytes(&certificate_path)?; 67 let certificate = Certificate::from_pem(&certificate_bytes).map_err(|error| Error::FileIo { 68 kind: FileKind::File, 69 action: FileIoAction::Parse, 70 path: Utf8PathBuf::from(&certificate_path), 71 err: Some(error.to_string()), 72 })?; 73 74 Ok(REQWEST_CLIENT.get_or_init(|| { 75 Client::builder() 76 .add_root_certificate(certificate) 77 .build() 78 .expect("Failed to create reqwest client") 79 })) 80}