@@ -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 ) ,
@@ -107,6 +111,13 @@ fn try_get_header<K: http::header::AsHeaderName>(
107111 headers. get ( key) . and_then ( |x| x. to_str ( ) . ok ( ) )
108112}
109113
114+ fn resolve_uri ( base : & Uri , relative : & str ) -> Option < Uri > {
115+ let base_abs = UriAbsoluteString :: try_from ( base. to_string ( ) ) . ok ( ) ?;
116+ let location_ref = UriReferenceStr :: new ( relative) . ok ( ) ?;
117+ let resolved = location_ref. resolve_against ( & base_abs) . to_string ( ) ;
118+ Uri :: try_from ( resolved) . ok ( )
119+ }
120+
110121impl < ' a , Http > AcmeClient < ' a , Http >
111122where
112123 Http : HttpClient ,
@@ -170,12 +181,27 @@ where
170181 }
171182
172183 pub async fn get ( & self , url : & Uri ) -> Result < http:: Response < Bytes > , RequestError > {
173- let req = http:: Request :: builder ( )
174- . uri ( url)
175- . method ( http:: Method :: GET )
176- . header ( http:: header:: CONTENT_LENGTH , 0 )
177- . body ( String :: new ( ) ) ?;
178- Ok ( self . http . request ( req) . await ?)
184+ let mut u = url. clone ( ) ;
185+
186+ for _ in 0 ..MAX_REDIRECTS {
187+ let req = http:: Request :: builder ( )
188+ . uri ( & u)
189+ . method ( http:: Method :: GET )
190+ . header ( http:: header:: CONTENT_LENGTH , 0 )
191+ . body ( String :: new ( ) ) ?;
192+ let res = self . http . request ( req) . await ?;
193+
194+ if res. status ( ) . is_redirection ( ) {
195+ let location = try_get_header ( res. headers ( ) , http:: header:: LOCATION )
196+ . ok_or ( RedirectError :: MissingRedirectUri ) ?;
197+ u = resolve_uri ( & u, location) . ok_or ( RedirectError :: InvalidRedirectUri ) ?;
198+ continue ;
199+ }
200+
201+ return Ok ( res) ;
202+ }
203+
204+ Err ( RedirectError :: TooManyRedirects . into ( ) )
179205 }
180206
181207 pub async fn post < P : AsRef < [ u8 ] > > (
0 commit comments