Skip to content

Commit a4970d2

Browse files
committed
updating redirect implementation
1 parent 026aace commit a4970d2

3 files changed

Lines changed: 343 additions & 18 deletions

File tree

components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RedirectHandler.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ boolean isRedirected(
8181
return false;
8282
}
8383

84-
Request getRedirect(final Request request, final Response userResponse)
84+
Request getRedirect(
85+
final Request request,
86+
final Response userResponse,
87+
final RedirectHandlerOption redirectOption,
88+
final Chain chain)
8589
throws ProtocolException {
8690
String location = userResponse.header("Location");
8791
if (location == null || location.length() == 0) return null;
@@ -105,18 +109,12 @@ Request getRedirect(final Request request, final Response userResponse)
105109
// Most redirects don't include a request body.
106110
Request.Builder requestBuilder = userResponse.request().newBuilder();
107111

108-
// When redirecting across hosts, drop all authentication headers. This
109-
// is potentially annoying to the application layer since they have no
110-
// way to retain them.
111-
boolean sameScheme = locationUrl.scheme().equalsIgnoreCase(requestUrl.scheme());
112-
boolean sameHost =
113-
locationUrl.host().toString().equalsIgnoreCase(requestUrl.host().toString());
114-
boolean samePort = locationUrl.port() == requestUrl.port();
115-
if (!sameScheme || !sameHost || !samePort) {
116-
requestBuilder.removeHeader("Authorization");
117-
requestBuilder.removeHeader("Cookie");
118-
requestBuilder.removeHeader("Proxy-Authorization");
119-
}
112+
// Scrub sensitive headers before following the redirect
113+
java.util.function.Function<HttpUrl, java.net.Proxy> proxyResolver =
114+
RedirectHandlerOption.getProxyResolver(
115+
chain.call().client().proxySelector());
116+
redirectOption.scrubSensitiveHeaders().scrubHeaders(
117+
requestBuilder, requestUrl, locationUrl, proxyResolver);
120118

121119
// Response status code 303 See Other then POST changes to GET
122120
if (userResponse.code() == HTTP_SEE_OTHER) {
@@ -166,7 +164,8 @@ Request getRedirect(final Request request, final Response userResponse)
166164
isRedirected(request, response, requestsCount, redirectOption)
167165
&& redirectOption.shouldRedirect().shouldRedirect(response);
168166

169-
final Request followup = shouldRedirect ? getRedirect(request, response) : null;
167+
final Request followup =
168+
shouldRedirect ? getRedirect(request, response, redirectOption, chain) : null;
170169
if (followup != null) {
171170
response.close();
172171
request = followup;

components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/options/RedirectHandlerOption.java

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
import jakarta.annotation.Nonnull;
66
import 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

Comments
 (0)