Check for signature errors, record signing strings
This commit is contained in:
		
							parent
							
								
									7e01cbfc41
								
							
						
					
					
						commit
						f55ea0a550
					
				
					 2 changed files with 63 additions and 69 deletions
				
			
		|  | @ -100,6 +100,9 @@ pub(crate) enum ErrorKind { | |||
|     #[error("Couldn't sign digest")] | ||||
|     Signature(#[from] signature::Error), | ||||
| 
 | ||||
|     #[error("Server {0} failed to validate our signature")] | ||||
|     SignedDelivery(String), | ||||
| 
 | ||||
|     #[error("Couldn't parse the signature header")] | ||||
|     HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue), | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										129
									
								
								src/requests.rs
									
										
									
									
									
								
							
							
						
						
									
										129
									
								
								src/requests.rs
									
										
									
									
									
								
							|  | @ -1,7 +1,7 @@ | |||
| use crate::error::{Error, ErrorKind}; | ||||
| use activitystreams::iri_string::types::IriString; | ||||
| use actix_web::{http::header::Date, web::Bytes}; | ||||
| use awc::Client; | ||||
| use awc::{Client, ClientResponse}; | ||||
| use dashmap::DashMap; | ||||
| use http_signature_normalization_actix::prelude::*; | ||||
| use rand::thread_rng; | ||||
|  | @ -53,6 +53,9 @@ impl Breakers { | |||
|             let should_write = { | ||||
|                 if let Some(mut breaker) = self.inner.get_mut(authority) { | ||||
|                     breaker.fail(); | ||||
|                     if !breaker.should_try() { | ||||
|                         tracing::warn!("Failed breaker for {}", authority); | ||||
|                     } | ||||
|                     false | ||||
|                 } else { | ||||
|                     true | ||||
|  | @ -101,17 +104,12 @@ struct Breaker { | |||
| } | ||||
| 
 | ||||
| impl Breaker { | ||||
|     const fn failure_threshold() -> usize { | ||||
|         10 | ||||
|     } | ||||
| 
 | ||||
|     fn failure_wait() -> Duration { | ||||
|         Duration::from_secs(ONE_DAY) | ||||
|     } | ||||
|     const FAILURE_WAIT: Duration = Duration::from_secs(ONE_DAY); | ||||
|     const FAILURE_THRESHOLD: usize = 10; | ||||
| 
 | ||||
|     fn should_try(&self) -> bool { | ||||
|         self.failures < Self::failure_threshold() | ||||
|             || self.last_attempt + Self::failure_wait() < SystemTime::now() | ||||
|         self.failures < Self::FAILURE_THRESHOLD | ||||
|             || self.last_attempt + Self::FAILURE_WAIT < SystemTime::now() | ||||
|     } | ||||
| 
 | ||||
|     fn fail(&mut self) { | ||||
|  | @ -192,7 +190,7 @@ impl Requests { | |||
|     fn count_err(&self) { | ||||
|         let count = self.consecutive_errors.fetch_add(1, Ordering::Relaxed); | ||||
|         if count + 1 >= self.error_limit { | ||||
|             tracing::warn!("{} consecutive errors, rebuilding http client", count); | ||||
|             tracing::warn!("{} consecutive errors, rebuilding http client", count + 1); | ||||
|             *self.client.borrow_mut() = build_client(&self.user_agent); | ||||
|             self.reset_err(); | ||||
|         } | ||||
|  | @ -202,7 +200,36 @@ impl Requests { | |||
|         self.consecutive_errors.swap(0, Ordering::Relaxed); | ||||
|     } | ||||
| 
 | ||||
|     #[tracing::instrument(name = "Fetch Json", skip(self))] | ||||
|     async fn check_response( | ||||
|         &self, | ||||
|         parsed_url: &IriString, | ||||
|         res: &mut ClientResponse, | ||||
|     ) -> Result<(), Error> { | ||||
|         self.reset_err(); | ||||
| 
 | ||||
|         if !res.status().is_success() { | ||||
|             self.breakers.fail(&parsed_url); | ||||
| 
 | ||||
|             if let Ok(bytes) = res.body().await { | ||||
|                 if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { | ||||
|                     if s.to_lowercase().contains("http signature") { | ||||
|                         return Err(ErrorKind::SignedDelivery(parsed_url.to_string()).into()); | ||||
|                     } | ||||
|                     if !s.is_empty() { | ||||
|                         tracing::warn!("Response from {}, {}", parsed_url, s); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return Err(ErrorKind::Status(parsed_url.to_string(), res.status()).into()); | ||||
|         } | ||||
| 
 | ||||
|         self.breakers.succeed(&parsed_url); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     #[tracing::instrument(name = "Fetch Json", skip(self), fields(signing_string))] | ||||
|     pub(crate) async fn fetch_json<T>(&self, url: &str) -> Result<T, Error> | ||||
|     where | ||||
|         T: serde::de::DeserializeOwned, | ||||
|  | @ -210,7 +237,7 @@ impl Requests { | |||
|         self.do_fetch(url, "application/json").await | ||||
|     } | ||||
| 
 | ||||
|     #[tracing::instrument(name = "Fetch Activity+Json", skip(self))] | ||||
|     #[tracing::instrument(name = "Fetch Activity+Json", skip(self), fields(signing_string))] | ||||
|     pub(crate) async fn fetch<T>(&self, url: &str) -> Result<T, Error> | ||||
|     where | ||||
|         T: serde::de::DeserializeOwned, | ||||
|  | @ -229,6 +256,7 @@ impl Requests { | |||
|         } | ||||
| 
 | ||||
|         let signer = self.signer(); | ||||
|         let span = tracing::Span::current(); | ||||
| 
 | ||||
|         let client: Client = self.client.borrow().clone(); | ||||
|         let res = client | ||||
|  | @ -238,7 +266,10 @@ impl Requests { | |||
|             .signature( | ||||
|                 self.config.clone(), | ||||
|                 self.key_id.clone(), | ||||
|                 move |signing_string| signer.sign(signing_string), | ||||
|                 move |signing_string| { | ||||
|                     span.record("signing_string", signing_string); | ||||
|                     span.in_scope(|| signer.sign(signing_string)) | ||||
|                 }, | ||||
|             ) | ||||
|             .await? | ||||
|             .send() | ||||
|  | @ -251,23 +282,7 @@ impl Requests { | |||
| 
 | ||||
|         let mut res = res.map_err(|e| ErrorKind::SendRequest(url.to_string(), e.to_string()))?; | ||||
| 
 | ||||
|         self.reset_err(); | ||||
| 
 | ||||
|         if !res.status().is_success() { | ||||
|             if let Ok(bytes) = res.body().await { | ||||
|                 if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { | ||||
|                     if !s.is_empty() { | ||||
|                         tracing::warn!("Response from {}, {}", url, s); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             self.breakers.fail(&parsed_url); | ||||
| 
 | ||||
|             return Err(ErrorKind::Status(url.to_string(), res.status()).into()); | ||||
|         } | ||||
| 
 | ||||
|         self.breakers.succeed(&parsed_url); | ||||
|         self.check_response(&parsed_url, &mut res).await?; | ||||
| 
 | ||||
|         let body = res | ||||
|             .body() | ||||
|  | @ -277,7 +292,7 @@ impl Requests { | |||
|         Ok(serde_json::from_slice(body.as_ref())?) | ||||
|     } | ||||
| 
 | ||||
|     #[tracing::instrument(name = "Fetch Bytes", skip(self))] | ||||
|     #[tracing::instrument(name = "Fetch Bytes", skip(self), fields(signing_string))] | ||||
|     pub(crate) async fn fetch_bytes(&self, url: &str) -> Result<(String, Bytes), Error> { | ||||
|         let parsed_url = url.parse::<IriString>()?; | ||||
| 
 | ||||
|  | @ -285,8 +300,8 @@ impl Requests { | |||
|             return Err(ErrorKind::Breaker.into()); | ||||
|         } | ||||
| 
 | ||||
|         tracing::info!("Fetching bytes for {}", url); | ||||
|         let signer = self.signer(); | ||||
|         let span = tracing::Span::current(); | ||||
| 
 | ||||
|         let client: Client = self.client.borrow().clone(); | ||||
|         let res = client | ||||
|  | @ -296,7 +311,10 @@ impl Requests { | |||
|             .signature( | ||||
|                 self.config.clone(), | ||||
|                 self.key_id.clone(), | ||||
|                 move |signing_string| signer.sign(signing_string), | ||||
|                 move |signing_string| { | ||||
|                     span.record("signing_string", signing_string); | ||||
|                     span.in_scope(|| signer.sign(signing_string)) | ||||
|                 }, | ||||
|             ) | ||||
|             .await? | ||||
|             .send() | ||||
|  | @ -309,7 +327,7 @@ impl Requests { | |||
| 
 | ||||
|         let mut res = res.map_err(|e| ErrorKind::SendRequest(url.to_string(), e.to_string()))?; | ||||
| 
 | ||||
|         self.reset_err(); | ||||
|         self.check_response(&parsed_url, &mut res).await?; | ||||
| 
 | ||||
|         let content_type = if let Some(content_type) = res.headers().get("content-type") { | ||||
|             if let Ok(s) = content_type.to_str() { | ||||
|  | @ -321,22 +339,6 @@ impl Requests { | |||
|             return Err(ErrorKind::ContentType.into()); | ||||
|         }; | ||||
| 
 | ||||
|         if !res.status().is_success() { | ||||
|             if let Ok(bytes) = res.body().await { | ||||
|                 if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { | ||||
|                     if !s.is_empty() { | ||||
|                         tracing::warn!("Response from {}, {}", url, s); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             self.breakers.fail(&parsed_url); | ||||
| 
 | ||||
|             return Err(ErrorKind::Status(url.to_string(), res.status()).into()); | ||||
|         } | ||||
| 
 | ||||
|         self.breakers.succeed(&parsed_url); | ||||
| 
 | ||||
|         let bytes = match res.body().limit(1024 * 1024 * 4).await { | ||||
|             Err(e) => { | ||||
|                 return Err(ErrorKind::ReceiveResponse(url.to_string(), e.to_string()).into()); | ||||
|  | @ -350,7 +352,7 @@ impl Requests { | |||
|     #[tracing::instrument(
 | ||||
|         "Deliver to Inbox", | ||||
|         skip_all, | ||||
|         fields(inbox = inbox.to_string().as_str()) | ||||
|         fields(inbox = inbox.to_string().as_str(), signing_string) | ||||
|     )] | ||||
|     pub(crate) async fn deliver<T>(&self, inbox: IriString, item: &T) -> Result<(), Error> | ||||
|     where | ||||
|  | @ -361,6 +363,7 @@ impl Requests { | |||
|         } | ||||
| 
 | ||||
|         let signer = self.signer(); | ||||
|         let span = tracing::Span::current(); | ||||
|         let item_string = serde_json::to_string(item)?; | ||||
| 
 | ||||
|         let client: Client = self.client.borrow().clone(); | ||||
|  | @ -374,7 +377,10 @@ impl Requests { | |||
|                 self.key_id.clone(), | ||||
|                 Sha256::new(), | ||||
|                 item_string, | ||||
|                 move |signing_string| signer.sign(signing_string), | ||||
|                 move |signing_string| { | ||||
|                     span.record("signing_string", signing_string); | ||||
|                     span.in_scope(|| signer.sign(signing_string)) | ||||
|                 }, | ||||
|             ) | ||||
|             .await? | ||||
|             .split(); | ||||
|  | @ -388,22 +394,7 @@ impl Requests { | |||
| 
 | ||||
|         let mut res = res.map_err(|e| ErrorKind::SendRequest(inbox.to_string(), e.to_string()))?; | ||||
| 
 | ||||
|         self.reset_err(); | ||||
| 
 | ||||
|         if !res.status().is_success() { | ||||
|             if let Ok(bytes) = res.body().await { | ||||
|                 if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) { | ||||
|                     if !s.is_empty() { | ||||
|                         tracing::warn!("Response from {}, {}", inbox.as_str(), s); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             self.breakers.fail(&inbox); | ||||
|             return Err(ErrorKind::Status(inbox.to_string(), res.status()).into()); | ||||
|         } | ||||
| 
 | ||||
|         self.breakers.succeed(&inbox); | ||||
|         self.check_response(&inbox, &mut res).await?; | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue