Browse Source

Add a basic HTTP client

ng
Rodolphe Bréard 4 weeks ago
parent
commit
11301a92c0
Failed to extract signature
  1. 1661
      Cargo.lock
  2. 6
      Cargo.toml
  3. 76
      build.rs
  4. 75
      src/http.rs
  5. 26
      src/main.rs

1661
Cargo.lock
File diff suppressed because it is too large
View File

6
Cargo.toml

@ -9,6 +9,7 @@ repository = "https://github.com/breard-r/acmed"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
keywords = ["acme", "tls", "X.509"] keywords = ["acme", "tls", "X.509"]
categories = ["cryptography"] categories = ["cryptography"]
build = "build.rs"
publish = false publish = false
rust-version = "1.74.0" rust-version = "1.74.0"
@ -26,9 +27,10 @@ clap = { version = "4.5.23", default-features = false, features = ["color", "der
config = { version = "0.14.0", default-features = false, features = ["toml"] } config = { version = "0.14.0", default-features = false, features = ["toml"] }
daemonize = { version = "0.5.0", default-features = false } daemonize = { version = "0.5.0", default-features = false }
nom = { version = "7.1.3", default-features = false } nom = { version = "7.1.3", default-features = false }
reqwest = { version = "0.12.10", default-features = false, features = ["http2", "default-tls", "charset", "rustls-tls"] }
serde = { version = "1.0.216", default-features = false, features = ["derive"] } serde = { version = "1.0.216", default-features = false, features = ["derive"] }
syslog-tracing = { version = "0.3.1", default-features = false } syslog-tracing = { version = "0.3.1", default-features = false }
tokio = { version = "1.42.0", default-features = false, features = ["rt", "rt-multi-thread", "time"] }
tokio = { version = "1.42.0", default-features = false, features = ["rt", "rt-multi-thread", "time", "sync"] }
tracing = { version = "0.1.41", default-features = false, features = ["std"] } tracing = { version = "0.1.41", default-features = false, features = ["std"] }
tracing-subscriber = { version = "0.3.19", default-features = false, features = ["ansi", "fmt", "std"] } tracing-subscriber = { version = "0.3.19", default-features = false, features = ["ansi", "fmt", "std"] }
walkdir = { version = "2.5.0", default-features = false } walkdir = { version = "2.5.0", default-features = false }
@ -36,6 +38,8 @@ walkdir = { version = "2.5.0", default-features = false }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
[build-dependencies] [build-dependencies]
serde = { version = "1.0.216", default-features = false, features = ["derive"] }
toml = "0.8.12"
[profile.release] [profile.release]
opt-level = "z" opt-level = "z"

76
build.rs

@ -0,0 +1,76 @@
extern crate serde;
extern crate toml;
use serde::Deserialize;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
macro_rules! set_rustc_env_var {
($name: expr, $value: expr) => {{
println!("cargo:rustc-env={}={}", $name, $value);
}};
}
#[derive(Deserialize)]
pub struct LockFile {
package: Vec<Package>,
}
#[derive(Deserialize)]
struct Package {
name: String,
version: String,
}
struct Error;
impl From<std::io::Error> for Error {
fn from(_error: std::io::Error) -> Self {
Error {}
}
}
impl From<toml::de::Error> for Error {
fn from(_error: toml::de::Error) -> Self {
Error {}
}
}
fn get_lock_file() -> Result<LockFile, Error> {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("Cargo.lock");
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let ret: LockFile = toml::from_str(&contents)?;
Ok(ret)
}
fn set_http_agent() {
let lock = match get_lock_file() {
Ok(l) => l,
Err(_) => {
return;
}
};
for p in lock.package.iter() {
if p.name == "reqwest" {
let agent = format!("{}/{}", p.name, p.version);
set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent);
return;
}
}
}
fn set_target() {
if let Ok(target) = env::var("TARGET") {
set_rustc_env_var!("ACMED_TARGET", target);
};
}
fn main() {
set_target();
set_http_agent();
}

75
src/http.rs

@ -0,0 +1,75 @@
use crate::config::AcmedConfig;
use anyhow::{Context, Result};
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{header, Client, ClientBuilder, Request, Response};
use tokio::sync::{mpsc, oneshot};
#[derive(Debug)]
struct RawRequest {
request: Request,
tx: oneshot::Sender<Result<Response, reqwest::Error>>,
}
#[derive(Clone, Debug)]
pub struct HttpClient {
tx: mpsc::UnboundedSender<RawRequest>,
}
impl HttpClient {
pub async fn send(&self, request: Request) -> Result<Response> {
let (tx, rx) = oneshot::channel();
let raw_req = RawRequest { request, tx };
self.tx.send(raw_req)?;
let ret = rx.await?;
if let Err(ref e) = ret {
tracing::error!("http error: {e:#?}");
}
Ok(ret?)
}
}
#[derive(Debug)]
pub(super) struct HttpRoutine {
tx: mpsc::UnboundedSender<RawRequest>,
rx: mpsc::UnboundedReceiver<RawRequest>,
}
impl HttpRoutine {
pub(super) fn new(config: &AcmedConfig) -> Self {
let (tx, mut rx) = mpsc::unbounded_channel();
Self { tx, rx }
}
pub(super) fn get_client(&self) -> HttpClient {
HttpClient {
tx: self.tx.clone(),
}
}
pub(super) async fn run(mut self) {
tracing::trace!("starting the http routine");
let client = self.get_http_client();
while let Some(raw_req) = self.rx.recv().await {
tracing::debug!("new http request: {:?}", raw_req.request);
let ret = client.execute(raw_req.request).await;
let _ = raw_req.tx.send(ret);
}
tracing::warn!("the http routine has stopped");
}
fn get_http_client(&self) -> Client {
let useragent = format!(
"{}/{} ({}) {}",
env!("CARGO_BIN_NAME"),
env!("CARGO_PKG_VERSION"),
env!("ACMED_TARGET"),
env!("ACMED_HTTP_LIB_AGENT")
);
let mut client_builder = ClientBuilder::new();
let mut default_headers = HeaderMap::new();
default_headers.append(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5".parse().unwrap());
default_headers.append(header::USER_AGENT, useragent.parse().unwrap());
client_builder = client_builder.default_headers(default_headers);
client_builder.build().unwrap()
}
}

26
src/main.rs

@ -1,8 +1,10 @@
mod cli; mod cli;
mod config; mod config;
mod http;
mod log; mod log;
use crate::config::AcmedConfig; use crate::config::AcmedConfig;
use crate::http::{HttpClient, HttpRoutine};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
use daemonize::Daemonize; use daemonize::Daemonize;
@ -47,6 +49,30 @@ fn main() {
async fn start(cnf: AcmedConfig) { async fn start(cnf: AcmedConfig) {
tracing::info!("starting ACMEd"); tracing::info!("starting ACMEd");
// Start the HTTP routine
let http_routine = HttpRoutine::new(&cnf);
let http_client = http_routine.get_client();
tokio::spawn(async move {
http_routine.run().await;
});
// TODO: REMOVE ME
use reqwest::{Method, Request, Url};
let rsp = http_client
.send(Request::new(
Method::GET,
Url::parse("https://example.org").unwrap(),
))
.await;
tracing::debug!("{rsp:?}");
let rsp = http_client
.send(Request::new(
Method::GET,
Url::parse("https://example.com/directory/").unwrap(),
))
.await;
tracing::debug!("{rsp:?}");
} }
fn init_server(foreground: bool, pid_file: Option<&Path>) { fn init_server(foreground: bool, pid_file: Option<&Path>) {

Loading…
Cancel
Save