66
77use std:: { error:: Error as StdError , fmt, sync:: Arc } ;
88
9- use http:: { Extensions , HeaderMap , HeaderValue , StatusCode , Uri } ;
9+ use bytes:: Bytes ;
10+ use http:: { Extensions , HeaderMap , HeaderValue , StatusCode , Uri , uri:: Authority } ;
1011
1112use crate :: {
1213 client:: {
@@ -28,7 +29,7 @@ use crate::{
2829/// redirect hops in a chain.
2930/// - `none` can be used to disable all redirect behavior.
3031/// - `custom` can be used to create a customized policy.
31- #[ derive( Clone ) ]
32+ #[ derive( Debug , Clone ) ]
3233pub struct Policy {
3334 inner : PolicyKind ,
3435}
@@ -38,6 +39,7 @@ pub struct Policy {
3839#[ derive( Debug ) ]
3940pub struct Attempt < ' a > {
4041 status : StatusCode ,
42+ headers : & ' a HeaderMap ,
4143 next : & ' a Uri ,
4244 previous : & ' a [ Uri ] ,
4345}
@@ -146,9 +148,16 @@ impl Policy {
146148 }
147149 }
148150
149- pub ( crate ) fn check ( & self , status : StatusCode , next : & Uri , previous : & [ Uri ] ) -> ActionKind {
151+ fn check (
152+ & self ,
153+ status : StatusCode ,
154+ headers : & HeaderMap ,
155+ next : & Uri ,
156+ previous : & [ Uri ] ,
157+ ) -> ActionKind {
150158 self . redirect ( Attempt {
151159 status,
160+ headers,
152161 next,
153162 previous,
154163 } )
@@ -169,6 +178,11 @@ impl<'a> Attempt<'a> {
169178 self . status
170179 }
171180
181+ /// Get the headers of redirect.
182+ pub fn headers ( & self ) -> & HeaderMap {
183+ self . headers
184+ }
185+
172186 /// Get the next URI to redirect to.
173187 pub fn uri ( & self ) -> & Uri {
174188 self . next
@@ -212,12 +226,6 @@ enum PolicyKind {
212226 None ,
213227}
214228
215- impl fmt:: Debug for Policy {
216- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
217- f. debug_tuple ( "Policy" ) . field ( & self . inner ) . finish ( )
218- }
219- }
220-
221229impl fmt:: Debug for PolicyKind {
222230 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
223231 match * self {
@@ -235,21 +243,6 @@ pub(crate) enum ActionKind {
235243 Error ( BoxError ) ,
236244}
237245
238- fn remove_sensitive_headers ( headers : & mut HeaderMap , next : & Uri , previous : & [ Uri ] ) {
239- if let Some ( previous) = previous. last ( ) {
240- let cross_host = next. host ( ) != previous. host ( )
241- || next. port ( ) != previous. port ( )
242- || next. scheme ( ) != previous. scheme ( ) ;
243- if cross_host {
244- headers. remove ( AUTHORIZATION ) ;
245- headers. remove ( COOKIE ) ;
246- headers. remove ( "cookie2" ) ;
247- headers. remove ( PROXY_AUTHORIZATION ) ;
248- headers. remove ( WWW_AUTHENTICATE ) ;
249- }
250- }
251- }
252-
253246#[ derive( Debug ) ]
254247struct TooManyRedirects ;
255248
@@ -279,37 +272,17 @@ impl FollowRedirectPolicy {
279272 }
280273 }
281274
282- pub ( crate ) fn with_referer ( mut self , referer : bool ) -> Self {
275+ pub ( crate ) const fn with_referer ( mut self , referer : bool ) -> Self {
283276 self . referer = referer;
284277 self
285278 }
286279
287- pub ( crate ) fn with_https_only ( mut self , https_only : bool ) -> Self {
280+ pub ( crate ) const fn with_https_only ( mut self , https_only : bool ) -> Self {
288281 self . https_only = https_only;
289282 self
290283 }
291284}
292285
293- fn make_referer ( next : & Uri , previous : & Uri ) -> Option < HeaderValue > {
294- if next. is_http ( ) && previous. is_https ( ) {
295- return None ;
296- }
297-
298- let mut parts = previous. clone ( ) . into_parts ( ) ;
299- if let Some ( authority) = & mut parts. authority {
300- let host_port = authority. host ( ) ;
301- let port = authority. port ( ) ;
302- let new_authority = match port {
303- Some ( port) => format ! ( "{}:{}" , host_port, port) ,
304- None => host_port. to_string ( ) ,
305- } ;
306- parts. authority = Some ( new_authority. parse ( ) . ok ( ) ?) ;
307- }
308-
309- let referer = Uri :: from_parts ( parts) . ok ( ) ?;
310- referer. to_string ( ) . parse ( ) . ok ( )
311- }
312-
313286impl policy:: Policy < Body , BoxError > for FollowRedirectPolicy {
314287 fn redirect ( & mut self , attempt : & policy:: Attempt < ' _ > ) -> Result < policy:: Action , BoxError > {
315288 // Parse the next URI from the attempt.
@@ -326,7 +299,7 @@ impl policy::Policy<Body, BoxError> for FollowRedirectPolicy {
326299 . expect ( "FollowRedirectPolicy should always have a policy set" ) ;
327300
328301 // Check if the next URI is already in the list of URLs.
329- match policy. check ( attempt. status ( ) , next_uri, & self . uris ) {
302+ match policy. check ( attempt. status ( ) , attempt . headers ( ) , next_uri, & self . uris ) {
330303 ActionKind :: Follow => {
331304 // Validate the next URI's scheme.
332305 if !next_uri. is_http ( ) && !next_uri. is_https ( ) {
@@ -378,6 +351,43 @@ impl policy::Policy<Body, BoxError> for FollowRedirectPolicy {
378351 }
379352}
380353
354+ fn make_referer ( next : & Uri , previous : & Uri ) -> Option < HeaderValue > {
355+ if next. is_http ( ) && previous. is_https ( ) {
356+ return None ;
357+ }
358+
359+ let referer = {
360+ let mut parts = previous. clone ( ) . into_parts ( ) ;
361+ if let Some ( authority) = & mut parts. authority {
362+ let host = authority. host ( ) ;
363+ parts. authority = match authority. port ( ) {
364+ Some ( port) => {
365+ Authority :: from_maybe_shared ( Bytes :: from ( format ! ( "{host}:{port}" ) ) ) . ok ( )
366+ }
367+ None => host. parse ( ) . ok ( ) ,
368+ } ;
369+ }
370+ Uri :: from_parts ( parts) . ok ( ) ?
371+ } ;
372+
373+ HeaderValue :: from_maybe_shared ( Bytes :: from ( referer. to_string ( ) ) ) . ok ( )
374+ }
375+
376+ fn remove_sensitive_headers ( headers : & mut HeaderMap , next : & Uri , previous : & [ Uri ] ) {
377+ if let Some ( previous) = previous. last ( ) {
378+ let cross_host = next. host ( ) != previous. host ( )
379+ || next. port ( ) != previous. port ( )
380+ || next. scheme ( ) != previous. scheme ( ) ;
381+ if cross_host {
382+ headers. remove ( AUTHORIZATION ) ;
383+ headers. remove ( COOKIE ) ;
384+ headers. remove ( "cookie2" ) ;
385+ headers. remove ( PROXY_AUTHORIZATION ) ;
386+ headers. remove ( WWW_AUTHENTICATE ) ;
387+ }
388+ }
389+ }
390+
381391#[ cfg( test) ]
382392mod tests {
383393 use super :: * ;
@@ -390,14 +400,14 @@ mod tests {
390400 . map ( |i| Uri :: try_from ( & format ! ( "http://a.b/c/{i}" ) ) . unwrap ( ) )
391401 . collect :: < Vec < _ > > ( ) ;
392402
393- match policy. check ( StatusCode :: FOUND , & next, & previous) {
403+ match policy. check ( StatusCode :: FOUND , & HeaderMap :: new ( ) , & next, & previous) {
394404 ActionKind :: Follow => ( ) ,
395405 other => panic ! ( "unexpected {other:?}" ) ,
396406 }
397407
398408 previous. push ( Uri :: try_from ( "http://a.b.d/e/33" ) . unwrap ( ) ) ;
399409
400- match policy. check ( StatusCode :: FOUND , & next, & previous) {
410+ match policy. check ( StatusCode :: FOUND , & HeaderMap :: new ( ) , & next, & previous) {
401411 ActionKind :: Error ( err) if err. is :: < TooManyRedirects > ( ) => ( ) ,
402412 other => panic ! ( "unexpected {other:?}" ) ,
403413 }
@@ -409,7 +419,7 @@ mod tests {
409419 let next = Uri :: try_from ( "http://x.y/z" ) . unwrap ( ) ;
410420 let previous = vec ! [ Uri :: try_from( "http://a.b/c" ) . unwrap( ) ] ;
411421
412- match policy. check ( StatusCode :: FOUND , & next, & previous) {
422+ match policy. check ( StatusCode :: FOUND , & HeaderMap :: new ( ) , & next, & previous) {
413423 ActionKind :: Error ( err) if err. is :: < TooManyRedirects > ( ) => ( ) ,
414424 other => panic ! ( "unexpected {other:?}" ) ,
415425 }
@@ -426,13 +436,13 @@ mod tests {
426436 } ) ;
427437
428438 let next = Uri :: try_from ( "http://bar/baz" ) . unwrap ( ) ;
429- match policy. check ( StatusCode :: FOUND , & next, & [ ] ) {
439+ match policy. check ( StatusCode :: FOUND , & HeaderMap :: new ( ) , & next, & [ ] ) {
430440 ActionKind :: Follow => ( ) ,
431441 other => panic ! ( "unexpected {other:?}" ) ,
432442 }
433443
434444 let next = Uri :: try_from ( "http://foo/baz" ) . unwrap ( ) ;
435- match policy. check ( StatusCode :: FOUND , & next, & [ ] ) {
445+ match policy. check ( StatusCode :: FOUND , & HeaderMap :: new ( ) , & next, & [ ] ) {
436446 ActionKind :: Stop => ( ) ,
437447 other => panic ! ( "unexpected {other:?}" ) ,
438448 }
0 commit comments