Browse Source

Log HTTP responses and POST requests

pull/39/head
Rodolphe Breard 4 years ago
parent
commit
0c8b0d3e53
  1. 13
      acmed/src/acme_proto/http.rs
  2. 50
      acmed/src/http.rs

13
acmed/src/acme_proto/http.rs

@ -53,10 +53,8 @@ where
let url = endpoint.dir.new_account.clone(); let url = endpoint.dir.new_account.clone();
let response = http::post_jose(endpoint, root_certs, &url, data_builder)?; let response = http::post_jose(endpoint, root_certs, &url, data_builder)?;
let acc_uri = response let acc_uri = response
.headers()
.get(http::HEADER_LOCATION)
.get_header(http::HEADER_LOCATION)
.ok_or_else(|| Error::from("no account location found"))?; .ok_or_else(|| Error::from("no account location found"))?;
let acc_uri = http::header_to_string(&acc_uri)?;
let acc_resp = response.json::<AccountResponse>()?; let acc_resp = response.json::<AccountResponse>()?;
Ok((acc_resp, acc_uri)) Ok((acc_resp, acc_uri))
} }
@ -72,10 +70,8 @@ where
let url = endpoint.dir.new_order.clone(); let url = endpoint.dir.new_order.clone();
let response = http::post_jose(endpoint, root_certs, &url, data_builder)?; let response = http::post_jose(endpoint, root_certs, &url, data_builder)?;
let order_uri = response let order_uri = response
.headers()
.get(http::HEADER_LOCATION)
.ok_or_else(|| Error::from("no order location found"))?;
let order_uri = http::header_to_string(&order_uri)?;
.get_header(http::HEADER_LOCATION)
.ok_or_else(|| Error::from("no account location found"))?;
let order_resp = response.json::<Order>()?; let order_resp = response.json::<Order>()?;
Ok((order_resp, order_uri)) Ok((order_resp, order_uri))
} }
@ -169,6 +165,5 @@ where
http::CONTENT_TYPE_JOSE, http::CONTENT_TYPE_JOSE,
http::CONTENT_TYPE_PEM, http::CONTENT_TYPE_PEM,
)?; )?;
let crt_pem = response.text()?;
Ok(crt_pem)
Ok(response.body)
} }

50
acmed/src/http.rs

@ -13,6 +13,38 @@ pub const CONTENT_TYPE_PEM: &str = "application/pem-certificate-chain";
pub const HEADER_NONCE: &str = "Replay-Nonce"; pub const HEADER_NONCE: &str = "Replay-Nonce";
pub const HEADER_LOCATION: &str = "Location"; pub const HEADER_LOCATION: &str = "Location";
pub struct ValidHttpResponse {
headers: attohttpc::header::HeaderMap,
pub body: String,
}
impl ValidHttpResponse {
pub fn get_header(&self, name: &str) -> Option<String> {
match self.headers.get(name) {
Some(r) => match header_to_string(r) {
Ok(h) => Some(h),
Err(_) => None,
},
None => None,
}
}
pub fn json<T>(&self) -> Result<T, Error>
where
T: serde::de::DeserializeOwned,
{
serde_json::from_str(&self.body).map_err(Error::from)
}
fn from_response(response: Response) -> Result<Self, Error> {
let (_status, headers, body) = response.split();
let body = body.text()?;
log::trace!("HTTP response headers: {:?}", headers);
log::trace!("HTTP response body: {}", body);
Ok(ValidHttpResponse { headers, body })
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum HttpError { pub enum HttpError {
ApiError(HttpApiError), ApiError(HttpApiError),
@ -97,10 +129,10 @@ fn rate_limit(endpoint: &mut Endpoint) {
endpoint.rl.block_until_allowed(); endpoint.rl.block_until_allowed();
} }
pub fn header_to_string(header_value: &header::HeaderValue) -> Result<String, Error> {
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 header format"))?;
Ok(s.to_string()) Ok(s.to_string())
} }
@ -130,14 +162,14 @@ pub fn get(
endpoint: &mut Endpoint, endpoint: &mut Endpoint,
root_certs: &[String], root_certs: &[String],
url: &str, url: &str,
) -> Result<Response, HttpError> {
) -> Result<ValidHttpResponse, HttpError> {
let mut session = get_session(root_certs)?; let mut session = get_session(root_certs)?;
session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?; session.try_header(header::ACCEPT, CONTENT_TYPE_JSON)?;
rate_limit(endpoint); rate_limit(endpoint);
let response = session.get(url).send()?; let response = session.get(url).send()?;
update_nonce(endpoint, &response)?; update_nonce(endpoint, &response)?;
check_status(&response)?; check_status(&response)?;
Ok(response)
ValidHttpResponse::from_response(response).map_err(HttpError::from)
} }
pub fn post<F>( pub fn post<F>(
@ -147,7 +179,7 @@ pub fn post<F>(
data_builder: &F, data_builder: &F,
content_type: &str, content_type: &str,
accept: &str, accept: &str,
) -> Result<Response, HttpError>
) -> Result<ValidHttpResponse, HttpError>
where where
F: Fn(&str, &str) -> Result<String, Error>, F: Fn(&str, &str) -> Result<String, Error>,
{ {
@ -161,14 +193,16 @@ where
let nonce = &endpoint.nonce.clone().unwrap_or_default(); let nonce = &endpoint.nonce.clone().unwrap_or_default();
let body = data_builder(&nonce, url)?; let body = data_builder(&nonce, url)?;
rate_limit(endpoint); rate_limit(endpoint);
log::trace!("POST request body: {}", body);
let response = session.post(url).text(&body).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(_) => {
return Ok(response);
return ValidHttpResponse::from_response(response).map_err(HttpError::from);
} }
Err(_) => { Err(_) => {
let api_err = response.json::<HttpApiError>()?;
let resp = ValidHttpResponse::from_response(response)?;
let api_err = resp.json::<HttpApiError>()?;
let acme_err = api_err.get_acme_type(); let acme_err = api_err.get_acme_type();
if !acme_err.is_recoverable() { if !acme_err.is_recoverable() {
return Err(api_err.into()); return Err(api_err.into());
@ -185,7 +219,7 @@ pub fn post_jose<F>(
root_certs: &[String], root_certs: &[String],
url: &str, url: &str,
data_builder: &F, data_builder: &F,
) -> Result<Response, HttpError>
) -> Result<ValidHttpResponse, HttpError>
where where
F: Fn(&str, &str) -> Result<String, Error>, F: Fn(&str, &str) -> Result<String, Error>,
{ {

Loading…
Cancel
Save