Browse Source

Replace `reqwest` by `attohttpc`

`reqwest` is a very good crate, however ACMEd does not require most of
its functionalities. For this job, `attohttpc` is also great and comes
with much less dependencies.

rel #1
pull/31/head
Rodolphe Breard 4 years ago
parent
commit
501b1aa9d8
  1. 3
      CHANGELOG.md
  2. 3
      acme_common/Cargo.toml
  3. 4
      acme_common/src/crypto/openssl_certificate.rs
  4. 28
      acme_common/src/error.rs
  5. 2
      acmed/Cargo.toml
  6. 2
      acmed/build.rs
  7. 54
      acmed/src/http.rs

3
CHANGELOG.md

@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- System users and groups can now be specified by name in addition to uid/gid. - System users and groups can now be specified by name in addition to uid/gid.
### Changed
- The HTTP(S) part is now handled by `attohttpc` instead of `reqwest`.
## [0.8.0] - 2020-06-12 ## [0.8.0] - 2020-06-12

3
acme_common/Cargo.toml

@ -13,14 +13,15 @@ publish = false
name = "acme_common" name = "acme_common"
[dependencies] [dependencies]
attohttpc = { version = "0.15", default-features = false }
base64 = "0.12" base64 = "0.12"
daemonize = "0.4" daemonize = "0.4"
env_logger = "0.7" env_logger = "0.7"
handlebars = "3.0" handlebars = "3.0"
log = "0.4" log = "0.4"
native-tls = "0.2"
openssl = "0.10" openssl = "0.10"
punycode = "0.4" punycode = "0.4"
reqwest = { version = "0.10", features = ["blocking", "default-tls"] }
serde_json = "1.0" serde_json = "1.0"
syslog = "5.0" syslog = "5.0"
toml = "0.5" toml = "0.5"

4
acme_common/src/crypto/openssl_certificate.rs

@ -58,6 +58,10 @@ impl X509Certificate {
}) })
} }
pub fn from_pem_native(pem_data: &[u8]) -> Result<native_tls::Certificate, Error> {
Ok(native_tls::Certificate::from_pem(pem_data)?)
}
pub fn from_acme_ext(domain: &str, acme_ext: &str) -> Result<(KeyPair, Self), Error> { pub fn from_acme_ext(domain: &str, acme_ext: &str) -> Result<(KeyPair, Self), Error> {
let key_pair = gen_keypair(KeyType::EcdsaP256)?; let key_pair = gen_keypair(KeyType::EcdsaP256)?;
let inner_cert = gen_certificate(domain, &key_pair, acme_ext)?; let inner_cert = gen_certificate(domain, &key_pair, acme_ext)?;

28
acme_common/src/error.rs

@ -81,33 +81,27 @@ impl From<serde_json::error::Error> for Error {
} }
} }
impl From<attohttpc::Error> for Error {
fn from(error: attohttpc::Error) -> Self {
format!("HTTP error: {}", error).into()
}
}
impl From<handlebars::TemplateRenderError> for Error { impl From<handlebars::TemplateRenderError> for Error {
fn from(error: handlebars::TemplateRenderError) -> Self { fn from(error: handlebars::TemplateRenderError) -> Self {
format!("Template error: {}", error).into() format!("Template error: {}", error).into()
} }
} }
impl From<openssl::error::ErrorStack> for Error {
fn from(error: openssl::error::ErrorStack) -> Self {
impl From<native_tls::Error> for Error {
fn from(error: native_tls::Error) -> Self {
format!("{}", error).into() format!("{}", error).into()
} }
} }
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
format!("HTTP error: {}", error).into()
}
}
impl From<reqwest::header::InvalidHeaderName> for Error {
fn from(error: reqwest::header::InvalidHeaderName) -> Self {
format!("Invalid HTTP header name: {}", error).into()
}
}
impl From<reqwest::header::InvalidHeaderValue> for Error {
fn from(error: reqwest::header::InvalidHeaderValue) -> Self {
format!("Invalid HTTP header value: {}", error).into()
impl From<openssl::error::ErrorStack> for Error {
fn from(error: openssl::error::ErrorStack) -> Self {
format!("{}", error).into()
} }
} }

2
acmed/Cargo.toml

@ -14,12 +14,12 @@ publish = false
[dependencies] [dependencies]
acme_common = { path = "../acme_common" } acme_common = { path = "../acme_common" }
attohttpc = { version = "0.15", features = ["charsets", "json"] }
clap = "2.32" clap = "2.32"
handlebars = "3.0" handlebars = "3.0"
log = "0.4" log = "0.4"
nom = "5.0" nom = "5.0"
openssl-sys = "0.9" openssl-sys = "0.9"
reqwest = { version = "0.10", features = ["blocking", "default-tls", "json"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
toml = "0.5" toml = "0.5"

2
acmed/build.rs

@ -58,7 +58,7 @@ fn set_lock() {
} }
}; };
for p in lock.package.iter() { for p in lock.package.iter() {
if p.name == "reqwest" {
if p.name == "attohttpc" {
let agent = format!("{}/{}", p.name, p.version); let agent = format!("{}/{}", p.name, p.version);
set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent); set_rustc_env_var!("ACMED_HTTP_LIB_AGENT", agent);
set_rustc_env_var!("ACMED_HTTP_LIB_NAME", p.name); set_rustc_env_var!("ACMED_HTTP_LIB_NAME", p.name);

54
acmed/src/http.rs

@ -1,8 +1,8 @@
use crate::acme_proto::structs::HttpApiError; use crate::acme_proto::structs::HttpApiError;
use crate::endpoint::Endpoint; use crate::endpoint::Endpoint;
use acme_common::crypto::X509Certificate;
use acme_common::error::Error; use acme_common::error::Error;
use reqwest::blocking::{Client, Response};
use reqwest::header::{self, HeaderMap, HeaderValue};
use attohttpc::{charsets, header, Response, Session};
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::{thread, time}; use std::{thread, time};
@ -40,12 +40,9 @@ fn update_nonce(endpoint: &mut Endpoint, response: &Response) -> Result<(), Erro
} }
fn check_status(response: &Response) -> Result<(), Error> { fn check_status(response: &Response) -> Result<(), Error> {
if !response.is_success() {
let status = response.status(); let status = response.status();
if !status.is_success() {
let msg = status
.canonical_reason()
.unwrap_or("<no description provided>");
let msg = format!("HTTP error: {}: {}", status.as_u16(), msg);
let msg = format!("HTTP error: {}: {}", status.as_u16(), status.as_str());
return Err(msg.into()); return Err(msg.into());
} }
Ok(()) Ok(())
@ -55,14 +52,14 @@ fn rate_limit(endpoint: &mut Endpoint) {
endpoint.rl.block_until_allowed(); endpoint.rl.block_until_allowed();
} }
pub fn header_to_string(header_value: &HeaderValue) -> Result<String, Error> {
pub fn header_to_string(header_value: &header::HeaderValue) -> Result<String, Error> {
let s = header_value let s = header_value
.to_str() .to_str()
.map_err(|_| Error::from("Invalid nonce format."))?; .map_err(|_| Error::from("Invalid nonce format."))?;
Ok(s.to_string()) Ok(s.to_string())
} }
fn get_client(root_certs: &[String]) -> Result<Client, Error> {
fn get_session(root_certs: &[String]) -> Result<Session, Error> {
let useragent = format!( let useragent = format!(
"{}/{} ({}) {}", "{}/{} ({}) {}",
crate::APP_NAME, crate::APP_NAME,
@ -70,31 +67,25 @@ fn get_client(root_certs: &[String]) -> Result<Client, Error> {
env!("ACMED_TARGET"), env!("ACMED_TARGET"),
env!("ACMED_HTTP_LIB_AGENT") env!("ACMED_HTTP_LIB_AGENT")
); );
let mut headers = HeaderMap::with_capacity(2);
// TODO: allow to change the language // TODO: allow to change the language
headers.insert(
header::ACCEPT_LANGUAGE,
HeaderValue::from_static("en-US,en;q=0.5"),
);
headers.insert(header::USER_AGENT, HeaderValue::from_str(&useragent)?);
let mut client_builder = Client::builder().default_headers(headers).referer(false);
let mut session = Session::new();
session.default_charset(Some(charsets::UTF_8));
session.try_header(header::ACCEPT_LANGUAGE, "en-US,en;q=0.5")?;
session.try_header(header::USER_AGENT, &useragent)?;
for crt_file in root_certs.iter() { for crt_file in root_certs.iter() {
let mut buff = Vec::new(); let mut buff = Vec::new();
File::open(crt_file)?.read_to_end(&mut buff)?; File::open(crt_file)?.read_to_end(&mut buff)?;
let crt = reqwest::Certificate::from_pem(&buff)?;
client_builder = client_builder.add_root_certificate(crt);
let crt = X509Certificate::from_pem_native(&buff)?;
session.add_root_certificate(crt);
} }
let client = client_builder.build()?;
Ok(client)
Ok(session)
} }
pub fn get(endpoint: &mut Endpoint, root_certs: &[String], url: &str) -> Result<Response, Error> { pub fn get(endpoint: &mut Endpoint, root_certs: &[String], url: &str) -> Result<Response, Error> {
let client = get_client(root_certs)?;
let mut session = get_session(root_certs)?;
session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?;
rate_limit(endpoint); rate_limit(endpoint);
let response = client
.get(url)
.header(header::ACCEPT, HeaderValue::from_static(CONTENT_TYPE_JSON))
.send()?;
let response = session.get(url).send()?;
update_nonce(endpoint, &response)?; update_nonce(endpoint, &response)?;
check_status(&response)?; check_status(&response)?;
Ok(response) Ok(response)
@ -111,20 +102,17 @@ pub fn post<F>(
where where
F: Fn(&str, &str) -> Result<String, Error>, F: Fn(&str, &str) -> Result<String, Error>,
{ {
let client = get_client(root_certs)?;
let mut session = get_session(root_certs)?;
session.try_header(header::ACCEPT, accept)?;
session.try_header(header::CONTENT_TYPE, content_type)?;
if endpoint.nonce.is_none() { if endpoint.nonce.is_none() {
let _ = new_nonce(endpoint, root_certs); let _ = new_nonce(endpoint, root_certs);
} }
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY { for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
let nonce = &endpoint.nonce.clone().unwrap(); let nonce = &endpoint.nonce.clone().unwrap();
let body = data_builder(&nonce, url)?.into_bytes();
let body = data_builder(&nonce, url)?;
rate_limit(endpoint); rate_limit(endpoint);
let response = client
.post(url)
.body(body)
.header(header::ACCEPT, HeaderValue::from_str(accept)?)
.header(header::CONTENT_TYPE, HeaderValue::from_str(content_type)?)
.send()?;
let response = session.post(url).text(&body).send()?;
update_nonce(endpoint, &response)?; update_nonce(endpoint, &response)?;
match check_status(&response) { match check_status(&response) {
Ok(_) => { Ok(_) => {

Loading…
Cancel
Save