Skip to content

Commit dbb34d8

Browse files
mdegelbavshin-f5
authored andcommitted
Add redirect handling to AcmeClient and including relevant error reporting.
1 parent 2934b34 commit dbb34d8

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ http-body = "1.0.1"
1818
http-body-util = "0.1.3"
1919
http-serde = "2.1.1"
2020
hyper = { version = "1.6.0", features = ["client", "http1"] }
21+
iri-string = "0.7.9"
2122
libc = "0.2.174"
2223
nginx-sys = "0.5.0"
2324
ngx = { version = "0.5.0", features = ["async", "serde", "std"] }

src/acme.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use std::collections::VecDeque;
1010
use std::string::{String, ToString};
1111

1212
use bytes::Bytes;
13-
use error::{NewAccountError, NewCertificateError, RequestError};
13+
use error::{NewAccountError, NewCertificateError, RedirectError, RequestError};
1414
use http::Uri;
15+
use iri_string::types::{UriAbsoluteString, UriReferenceStr};
1516
use ngx::allocator::{Allocator, Box};
1617
use ngx::async_::sleep;
1718
use ngx::collections::Vec;
@@ -43,6 +44,9 @@ const MAX_SERVER_RETRY_INTERVAL: Duration = Duration::from_secs(60);
4344

4445
static REPLAY_NONCE: http::HeaderName = http::HeaderName::from_static("replay-nonce");
4546

47+
// Maximum number of redirects to follow for a single request.
48+
const MAX_REDIRECTS: usize = 10;
49+
4650
pub enum NewAccountOutput<'a> {
4751
Created(&'a str),
4852
Found(&'a str),
@@ -108,6 +112,13 @@ fn try_get_header<K: http::header::AsHeaderName>(
108112
headers.get(key).and_then(|x| x.to_str().ok())
109113
}
110114

115+
fn resolve_uri(base: &Uri, relative: &str) -> Option<Uri> {
116+
let base_abs = UriAbsoluteString::try_from(base.to_string()).ok()?;
117+
let location_ref = UriReferenceStr::new(relative).ok()?;
118+
let resolved = location_ref.resolve_against(&base_abs).to_string();
119+
Uri::try_from(resolved).ok()
120+
}
121+
111122
impl<'a, Http> AcmeClient<'a, Http>
112123
where
113124
Http: HttpClient,
@@ -172,12 +183,27 @@ where
172183
}
173184

174185
pub async fn get(&self, url: &Uri) -> Result<http::Response<Bytes>, RequestError> {
175-
let req = http::Request::builder()
176-
.uri(url)
177-
.method(http::Method::GET)
178-
.header(http::header::CONTENT_LENGTH, 0)
179-
.body(String::new())?;
180-
Ok(self.http.request(req).await?)
186+
let mut u = url.clone();
187+
188+
for _ in 0..MAX_REDIRECTS {
189+
let req = http::Request::builder()
190+
.uri(&u)
191+
.method(http::Method::GET)
192+
.header(http::header::CONTENT_LENGTH, 0)
193+
.body(String::new())?;
194+
let res = self.http.request(req).await?;
195+
196+
if res.status().is_redirection() {
197+
let location = try_get_header(res.headers(), http::header::LOCATION)
198+
.ok_or(RedirectError::MissingRedirectUri)?;
199+
u = resolve_uri(&u, location).ok_or(RedirectError::InvalidRedirectUri)?;
200+
continue;
201+
}
202+
203+
return Ok(res);
204+
}
205+
206+
Err(RedirectError::TooManyRedirects.into())
181207
}
182208

183209
pub async fn post<P: AsRef<[u8]>>(

src/acme/error.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ impl NewCertificateError {
114114
}
115115
}
116116

117+
#[derive(Debug, Error)]
118+
pub enum RedirectError {
119+
#[error("invalid redirect URI")]
120+
InvalidRedirectUri,
121+
122+
#[error("missing redirect URI")]
123+
MissingRedirectUri,
124+
125+
#[error("too many redirects")]
126+
TooManyRedirects,
127+
}
128+
117129
#[derive(Debug, Error)]
118130
pub enum RequestError {
119131
#[error(transparent)]
@@ -137,6 +149,9 @@ pub enum RequestError {
137149
#[error("rate limit exceeded, next attempt in {0:?}")]
138150
RateLimited(Duration),
139151

152+
#[error("redirect failed: {0}")]
153+
Redirect(#[from] RedirectError),
154+
140155
#[error("cannot serialize request ({0})")]
141156
RequestFormat(serde_json::Error),
142157

0 commit comments

Comments
 (0)