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

com.microsoft.graph.httpcore.RetryHandler Maven / Gradle / Ivy

There is a newer version: 3.0.61
Show newest version
package com.microsoft.graph.httpcore;

import java.io.IOException;

import com.microsoft.graph.httpcore.middlewareoption.IShouldRetry;
import com.microsoft.graph.httpcore.middlewareoption.MiddlewareType;
import com.microsoft.graph.httpcore.middlewareoption.RetryOptions;
import com.microsoft.graph.httpcore.middlewareoption.TelemetryOptions;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class RetryHandler implements Interceptor{

	public final MiddlewareType MIDDLEWARE_TYPE = MiddlewareType.RETRY;

    private RetryOptions mRetryOption;

    /*
     * constant string being used
     */
    private final String RETRY_ATTEMPT_HEADER = "Retry-Attempt";
    private final String RETRY_AFTER = "Retry-After";
    /** Content length request header value */
    private final String CONTENT_LENGTH = "Content-Length";

    public static final int MSClientErrorCodeTooManyRequests = 429;
    public static final int MSClientErrorCodeServiceUnavailable  = 503;
    public static final int MSClientErrorCodeGatewayTimeout = 504;

    private final long DELAY_MILLISECONDS = 1000;

    /*
     * @retryOption Create Retry handler using retry option
     */
    public RetryHandler(RetryOptions retryOption) {
        this.mRetryOption = retryOption;
        if(this.mRetryOption == null) {
        	this.mRetryOption = new RetryOptions();
        }
    }
    /*
     * Initialize retry handler with default retry option
     */
    public RetryHandler() {
        this(null);
    }

	boolean retryRequest(Response response, int executionCount, Request request, RetryOptions retryOptions) {

		// Should retry option
		// Use should retry common for all requests
		IShouldRetry shouldRetryCallback = null;
		if(retryOptions != null) {
			shouldRetryCallback = retryOptions.shouldRetry();
		}

		boolean shouldRetry = false;
		// Status codes 429 503 504
		int statusCode = response.code();
		// Only requests with payloads that are buffered/rewindable are supported.
		// Payloads with forward only streams will be have the responses returned
		// without any retry attempt.
		shouldRetry =
				(executionCount <= retryOptions.maxRetries())
				&& checkStatus(statusCode) && isBuffered(request)
				&& shouldRetryCallback != null
				&& shouldRetryCallback.shouldRetry(retryOptions.delay(), executionCount, request, response);

		if(shouldRetry) {
			long retryInterval = getRetryAfter(response, retryOptions.delay(), executionCount);
			try {
				Thread.sleep(retryInterval);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return shouldRetry;
	}

	/**
	 * Get retry after in milliseconds
	 * @param response Response
	 * @param delay Delay in seconds
	 * @param executionCount Execution count of retries
	 * @return Retry interval in milliseconds
	 */
	long getRetryAfter(Response response, long delay, int executionCount) {
		String retryAfterHeader = response.header(RETRY_AFTER);
		double retryDelay = RetryOptions.DEFAULT_DELAY * DELAY_MILLISECONDS;
		if(retryAfterHeader != null) {
			retryDelay = Long.parseLong(retryAfterHeader) * DELAY_MILLISECONDS;
		} else {
			retryDelay = (double)((Math.pow(2.0, (double)executionCount)-1)*0.5);
			retryDelay = (executionCount < 2 ? delay : retryDelay + delay) + (double)Math.random();
			retryDelay *= DELAY_MILLISECONDS;
		}
		return (long)Math.min(retryDelay, RetryOptions.MAX_DELAY * DELAY_MILLISECONDS);
	}

	boolean checkStatus(int statusCode) {
	    return (statusCode == MSClientErrorCodeTooManyRequests || statusCode == MSClientErrorCodeServiceUnavailable
	    		|| statusCode == MSClientErrorCodeGatewayTimeout);
	}

	boolean isBuffered(Request request) {
		final String methodName = request.method();

		final boolean isHTTPMethodPutPatchOrPost = methodName.equalsIgnoreCase("POST") ||
				methodName.equalsIgnoreCase("PUT") ||
				methodName.equalsIgnoreCase("PATCH");

		if(isHTTPMethodPutPatchOrPost && request.body() != null) {
            try {
                return request.body().contentLength() != -1L;
            } catch (IOException ex) {
                // expected
                return false;
            }
		}
		return true;
	}

	@Override
	public Response intercept(Chain chain) throws IOException {
		Request request = chain.request();

		if(request.tag(TelemetryOptions.class) == null)
			request = request.newBuilder().tag(TelemetryOptions.class, new TelemetryOptions()).build();
		request.tag(TelemetryOptions.class).setFeatureUsage(TelemetryOptions.RETRY_HANDLER_ENABLED_FLAG);

		Response response = chain.proceed(request);

		// Use should retry pass along with this request
		RetryOptions retryOption = request.tag(RetryOptions.class);
		retryOption = retryOption != null ? retryOption : mRetryOption;

		int executionCount = 1;
		while(retryRequest(response, executionCount, request, retryOption)) {
			request = request.newBuilder().addHeader(RETRY_ATTEMPT_HEADER, String.valueOf(executionCount)).build();
			executionCount++;
			if(response != null) {
                if(response.body() != null)
                    response.body().close();
                response.close();
            }
			response = chain.proceed(request);
		}
		return response;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy