55import jakarta .annotation .Nonnull ;
66import jakarta .annotation .Nullable ;
77
8+ import okhttp3 .HttpUrl ;
9+ import okhttp3 .Request ;
10+
11+ import java .net .InetSocketAddress ;
12+ import java .net .Proxy ;
13+ import java .net .ProxySelector ;
14+ import java .net .URI ;
15+ import java .util .List ;
16+ import java .util .Objects ;
17+ import java .util .function .Function ;
18+
819/**
920 * Options to be passed to the redirect middleware.
1021 */
@@ -28,11 +39,59 @@ public class RedirectHandlerOption implements RequestOption {
2839 */
2940 @ Nonnull public static final IShouldRedirect DEFAULT_SHOULD_REDIRECT = response -> true ;
3041
42+ @ Nonnull private final IScrubSensitiveHeaders scrubSensitiveHeaders ;
43+
44+ /**
45+ * Functional interface for scrubbing sensitive headers during redirects.
46+ */
47+ @ FunctionalInterface
48+ public interface IScrubSensitiveHeaders {
49+ /**
50+ * Scrubs sensitive headers from the request before following a redirect.
51+ * @param requestBuilder The request builder to modify
52+ * @param originalUrl The original request URL
53+ * @param newUrl The new redirect URL
54+ * @param proxyResolver A function that returns the proxy for a given destination, or null if no proxy applies
55+ */
56+ void scrubHeaders (
57+ @ Nonnull Request .Builder requestBuilder ,
58+ @ Nonnull HttpUrl originalUrl ,
59+ @ Nonnull HttpUrl newUrl ,
60+ @ Nullable Function <HttpUrl , Proxy > proxyResolver );
61+ }
62+
63+ /**
64+ * The default implementation for scrubbing sensitive headers during redirects.
65+ * This method removes Authorization and Cookie headers when the host or scheme changes,
66+ * and removes Proxy-Authorization headers when no proxy is configured or the proxy is bypassed for the new URL.
67+ */
68+ @ Nonnull public static final IScrubSensitiveHeaders DEFAULT_SCRUB_SENSITIVE_HEADERS =
69+ (requestBuilder , originalUrl , newUrl , proxyResolver ) -> {
70+ Objects .requireNonNull (requestBuilder , "parameter requestBuilder cannot be null" );
71+ Objects .requireNonNull (originalUrl , "parameter originalUrl cannot be null" );
72+ Objects .requireNonNull (newUrl , "parameter newUrl cannot be null" );
73+
74+ // Remove Authorization and Cookie headers if the request's scheme or host changes
75+ boolean isDifferentHostOrScheme =
76+ !newUrl .host ().equalsIgnoreCase (originalUrl .host ())
77+ || !newUrl .scheme ().equalsIgnoreCase (originalUrl .scheme ());
78+ if (isDifferentHostOrScheme ) {
79+ requestBuilder .removeHeader ("Authorization" );
80+ requestBuilder .removeHeader ("Cookie" );
81+ }
82+
83+ // Remove Proxy-Authorization if no proxy is configured or the URL is bypassed
84+ boolean isProxyInactive = proxyResolver == null || proxyResolver .apply (newUrl ) == null ;
85+ if (isProxyInactive ) {
86+ requestBuilder .removeHeader ("Proxy-Authorization" );
87+ }
88+ };
89+
3190 /**
3291 * Create default instance of redirect options, with default values of max redirects and should redirect
3392 */
3493 public RedirectHandlerOption () {
35- this (DEFAULT_MAX_REDIRECTS , DEFAULT_SHOULD_REDIRECT );
94+ this (DEFAULT_MAX_REDIRECTS , DEFAULT_SHOULD_REDIRECT , DEFAULT_SCRUB_SENSITIVE_HEADERS );
3695 }
3796
3897 /**
@@ -41,13 +100,30 @@ public RedirectHandlerOption() {
41100 * @param shouldRedirect Should redirect callback called before every redirect
42101 */
43102 public RedirectHandlerOption (int maxRedirects , @ Nullable final IShouldRedirect shouldRedirect ) {
103+ this (maxRedirects , shouldRedirect , DEFAULT_SCRUB_SENSITIVE_HEADERS );
104+ }
105+
106+ /**
107+ * Create an instance with provided values
108+ * @param maxRedirects Max redirects to occur
109+ * @param shouldRedirect Should redirect callback called before every redirect
110+ * @param scrubSensitiveHeaders Callback to scrub sensitive headers during redirects
111+ */
112+ public RedirectHandlerOption (
113+ int maxRedirects ,
114+ @ Nullable final IShouldRedirect shouldRedirect ,
115+ @ Nullable final IScrubSensitiveHeaders scrubSensitiveHeaders ) {
44116 if (maxRedirects < 0 )
45117 throw new IllegalArgumentException ("Max redirects cannot be negative" );
46118 if (maxRedirects > MAX_REDIRECTS )
47119 throw new IllegalArgumentException ("Max redirect cannot exceed " + MAX_REDIRECTS );
48120
49121 this .maxRedirects = maxRedirects ;
50122 this .shouldRedirect = shouldRedirect != null ? shouldRedirect : DEFAULT_SHOULD_REDIRECT ;
123+ this .scrubSensitiveHeaders =
124+ scrubSensitiveHeaders != null
125+ ? scrubSensitiveHeaders
126+ : DEFAULT_SCRUB_SENSITIVE_HEADERS ;
51127 }
52128
53129 /**
@@ -66,6 +142,40 @@ public int maxRedirects() {
66142 return this .shouldRedirect ;
67143 }
68144
145+ /**
146+ * Gets the callback for scrubbing sensitive headers during redirects.
147+ * @return scrub sensitive headers callback
148+ */
149+ @ Nonnull public IScrubSensitiveHeaders scrubSensitiveHeaders () {
150+ return this .scrubSensitiveHeaders ;
151+ }
152+
153+ /**
154+ * Helper method to get a proxy resolver from a ProxySelector.
155+ * @param proxySelector The ProxySelector to use, or null if no proxy is configured
156+ * @return A function that resolves proxies for a given HttpUrl, or null if no proxy selector is provided
157+ */
158+ @ Nullable public static Function <HttpUrl , Proxy > getProxyResolver (
159+ @ Nullable final ProxySelector proxySelector ) {
160+ if (proxySelector == null ) {
161+ return null ;
162+ }
163+ return url -> {
164+ try {
165+ URI uri = new URI (url .scheme (), null , url .host (), url .port (), null , null , null );
166+ List <Proxy > proxies = proxySelector .select (uri );
167+ if (proxies != null && !proxies .isEmpty ()) {
168+ Proxy proxy = proxies .get (0 );
169+ // Return null for DIRECT proxies (no proxy)
170+ return proxy .type () == Proxy .Type .DIRECT ? null : proxy ;
171+ }
172+ return null ;
173+ } catch (Exception e ) {
174+ return null ;
175+ }
176+ };
177+ }
178+
69179 /** {@inheritDoc} */
70180 @ SuppressWarnings ("unchecked" )
71181 @ Override
0 commit comments