All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jio.http.client.oauth.ClientCredentialsClient Maven / Gradle / Ivy

There is a newer version: 3.0.0-RC1
Show newest version
package jio.http.client.oauth;

import jio.IO;
import jio.Lambda;
import jio.http.client.HttpLambda;
import jio.http.client.JioHttpClient;
import jio.http.client.JioHttpClientBuilder;

import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * An HTTP client with support for OAuth Client Credentials Grant. This client allows you to make HTTP requests with
 * OAuth authentication using the Client Credentials Grant flow. It automatically handles access token expiration and
 * refreshes tokens when needed.
 */
final class ClientCredentialsClient implements OauthHttpClient {

  private static final int MAX_REFRESH_TOKEN_LOOP_SIZE = 3;
  private final JioHttpClient httpClient;
  private final Function>> accessTokenReq;
  private final String authorizationHeaderName;
  private final Function authorizationHeaderValue;
  private final Lambda, String> getAccessToken;
  private final Predicate> refreshTokenPredicate;
  private final HttpLambda oauthStringLambda;
  private final HttpLambda oauthBytesLambda;
  private final HttpLambda ofStringLambda;
  private final HttpLambda ofBytesLambda;
  private final HttpLambda discardingLambda;
  private final HttpLambda oauthDiscardingLambda;
  private volatile String accessToken;

  ClientCredentialsClient(final JioHttpClientBuilder client,
                          final Function>> accessTokenReq,
                          final String authorizationHeaderName,
                          final Function authorizationHeaderValue,
                          final Lambda, String> getAccessToken,
                          final Predicate> refreshTokenPredicate
                         ) {
    this.httpClient = client.get();
    this.accessTokenReq = accessTokenReq;
    this.authorizationHeaderName = authorizationHeaderName;
    this.authorizationHeaderValue = authorizationHeaderValue;
    this.getAccessToken = getAccessToken;
    this.refreshTokenPredicate = refreshTokenPredicate;
    this.ofStringLambda = httpClient.ofString();
    this.ofBytesLambda = httpClient.ofBytes();
    this.discardingLambda = httpClient.discarding();
    this.oauthDiscardingLambda = builder -> oauthRequest(discardingLambda,
                                                         builder,
                                                         false,
                                                         0
                                                        );
    this.oauthStringLambda = builder -> oauthRequest(ofStringLambda,
                                                     builder,
                                                     false,
                                                     0
                                                    );
    this.oauthBytesLambda = builder -> oauthRequest(ofBytesLambda,
                                                    builder,
                                                    false,
                                                    0
                                                   );
  }


  @Override
  public HttpLambda oauthOfString() {
    return oauthStringLambda;
  }

  @Override
  public HttpLambda oauthOfBytes() {
    return oauthBytesLambda;
  }

  @Override
  public HttpLambda oauthDiscarding() {
    return oauthDiscardingLambda;
  }

  @Override
  public  HttpLambda oauthBodyHandler(HttpResponse.BodyHandler handler) {
    return builder -> oauthRequest(bodyHandler(handler),
                                   builder,
                                   false,
                                   0
                                  );
  }

  @Override
  public HttpLambda ofString() {
    return ofStringLambda;
  }

  @Override
  public HttpLambda ofBytes() {
    return ofBytesLambda;
  }

  @Override
  public HttpLambda discarding() {
    return discardingLambda;
  }

  @Override
  public  HttpLambda bodyHandler(HttpResponse.BodyHandler handler) {
    return httpClient.bodyHandler(handler);
  }


  private  IO> oauthRequest(final HttpLambda httpLambda,
                                               final HttpRequest.Builder builder,
                                               final boolean refreshToken,
                                               final int deep
                                              ) {
      if (deep == MAX_REFRESH_TOKEN_LOOP_SIZE) {
          return IO.fail(new RefreshTokenLoop(deep));
      }

    IO getToken = (refreshToken || this.accessToken == null) ?
                          accessTokenReq.apply(this)
                                        .then(getAccessToken)
                                        .peekSuccess(newToken -> this.accessToken = newToken) :
                          IO.succeed(this.accessToken);

    return getToken.then(token ->
                             httpLambda.apply(builder.setHeader(authorizationHeaderName,
                                                                authorizationHeaderValue.apply(token)
                                                               )
                                             )
                                       .then(resp ->
                                                 refreshTokenPredicate.test(resp) ?
                                                 oauthRequest(httpLambda,
                                                              builder,
                                                              true,
                                                              deep + 1
                                                             ) :
                                                 IO.succeed(resp)
                                            )
                        );
  }

}