@@ -10,8 +10,9 @@ use std::collections::VecDeque;
1010use std:: string:: { String , ToString } ;
1111
1212use bytes:: Bytes ;
13- use error:: { NewAccountError , NewCertificateError , RequestError } ;
13+ use error:: { NewAccountError , NewCertificateError , RedirectError , RequestError } ;
1414use http:: Uri ;
15+ use iri_string:: types:: { UriAbsoluteString , UriReferenceStr } ;
1516use ngx:: allocator:: { Allocator , Box } ;
1617use ngx:: async_:: sleep;
1718use ngx:: collections:: Vec ;
@@ -43,6 +44,9 @@ const MAX_SERVER_RETRY_INTERVAL: Duration = Duration::from_secs(60);
4344
4445static 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+
4650pub 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+
111122impl < ' a , Http > AcmeClient < ' a , Http >
112123where
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 ] > > (
0 commit comments