-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Support Preemptive Authentication with RestClient #21336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Set<HttpHost> httpHosts = new HashSet<>(); | ||
for (HttpHost host : hosts) { | ||
Objects.requireNonNull(host, "host cannot be null"); | ||
httpHosts.add(host); | ||
authCache.put(host, new BasicScheme()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be volatile
and work in the same way as hosts
. Built, then set.
I wanted to avoid adding an unnecessary volatile
variable; BasicAuthCache
is thread safe, but clearing it technically means that an async request could be sent that then does not use preemptive authorization while the new hosts are being set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
volatile reads are cheap on x86; IIRC mapping to a no-op. I'd prefer volatile; but there is still the issue that there will be a small amount of time when hosts and the auth cache are not in sync (between setting both).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think we should hardcode the scheme. This makes the assumption that every host uses preemptive basic auth if basic auth credentials are provided. Some requests may not need preemptive authentication. For example, requests could be made with anonymous access, while others from the same client may require authentication. In that scenario you don't want/need preemptive auth but we are forcing it.
I think this should be opt-in like the default behavior of HttpClient. We can do this by providing the ability to specify a callback in the builder that when given a HttpHost it will provide the appropriate scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked with the other HTTP-based ES clients, and they all do it this way. So I think it's best if we match their behavior.
For anyone that does want to opt-out, it's very simple to do with the existing client builder by invoking:
httpClientBuilder.disableAuthCaching();
The better approach for users that expect the current behavior is to only supply credentials when they want to use them. This will give the general user the more common approach of using credentials when they're supplied rather than doubling the number of requests for the average auth-based user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but there is still the issue that there will be a small amount of time when hosts and the auth cache are not in sync (between setting both).
I'll make them a volatile Tuple
that get updated together. That way there's no opportunity for a dirty read.
Set<HttpHost> httpHosts = new HashSet<>(); | ||
for (HttpHost host : hosts) { | ||
Objects.requireNonNull(host, "host cannot be null"); | ||
httpHosts.add(host); | ||
authCache.put(host, new BasicScheme()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
volatile reads are cheap on x86; IIRC mapping to a no-op. I'd prefer volatile; but there is still the issue that there will be a small amount of time when hosts and the auth cache are not in sync (between setting both).
Set<HttpHost> httpHosts = new HashSet<>(); | ||
for (HttpHost host : hosts) { | ||
Objects.requireNonNull(host, "host cannot be null"); | ||
httpHosts.add(host); | ||
authCache.put(host, new BasicScheme()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think we should hardcode the scheme. This makes the assumption that every host uses preemptive basic auth if basic auth credentials are provided. Some requests may not need preemptive authentication. For example, requests could be made with anonymous access, while others from the same client may require authentication. In that scenario you don't want/need preemptive auth but we are forcing it.
I think this should be opt-in like the default behavior of HttpClient. We can do this by providing the ability to specify a callback in the builder that when given a HttpHost it will provide the appropriate scheme.
2efc089
to
56a5e38
Compare
please test this |
d265585
to
7b70f01
Compare
@@ -122,11 +126,13 @@ public synchronized void setHosts(HttpHost... hosts) { | |||
throw new IllegalArgumentException("hosts must not be null nor empty"); | |||
} | |||
Set<HttpHost> httpHosts = new HashSet<>(); | |||
AuthCache authCache = new BasicAuthCache(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think this might not be desired behavior. On every sniff the auth cache is invalidated completely. I think it would be better to add the hosts to the set. Then we should do a set difference to find the hosts that have been added and a set difference to find the hosts that have been removed. The added hosts get a new entry in the authcache and the removed ones get removed from the cache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we update the AuthCache
rather than replace it, then it will potentially impact an ongoing request. I checked out the BasicAuthCache
and BasicScheme
code and it looks pretty lightweight?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. We do not need to do this
This adds the necessary `AuthCache` needed to support preemptive authorization. By adding every host to the cache, the automatically added `RequestAuthCache` interceptor will add credentials on the first pass rather than waiting to do it after _each_ anonymous request is rejected (thus always sending everything twice when basic auth is required). Note: This currently has no tests because I wanted to make sure that my approach was sound. I did test it locally and it worked as expected.
7b70f01
to
e7075ac
Compare
This adds the necessary `AuthCache` needed to support preemptive authorization. By adding every host to the cache, the automatically added `RequestAuthCache` interceptor will add credentials on the first pass rather than waiting to do it after _each_ anonymous request is rejected (thus always sending everything twice when basic auth is required).
5.3: bc5d37b |
This adds the necessary
AuthCache
needed to support preemptive authentication. By adding every host to the cache, the automatically addedRequestAuthCache
interceptor will add credentials on the first pass rather than waiting to do it after each anonymous request is rejected (thus always sending everything twice when basic auth is required).It's easy to test with/without this using a nodejs proxy.
package.json
:Using npm, install necessary dependencies
Then run the proxy to intercept comms:
proxy.js:
/cc @javanna