diff --git a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java index 86a551e6b..81d34562b 100644 --- a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java +++ b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java @@ -141,14 +141,25 @@ long getRetryAfter(Response response, long delay, int executionCount) { return (long) Math.min(retryDelay, RetryHandlerOption.MAX_DELAY * DELAY_MILLISECONDS); } + /** + * Return first positive value from the Retry-After header. + * @param retryAfterHeader The value of the Retry-After header. Sometimes it can contain multiple values separated by commas. + * For example: "31,120". + * @return Retry interval in milliseconds + */ double tryParseTimeHeader(String retryAfterHeader) { - double retryDelay = -1; - try { - retryDelay = Integer.parseInt(retryAfterHeader) * DELAY_MILLISECONDS; - } catch (NumberFormatException e) { - return retryDelay; + String[] values = retryAfterHeader.split(","); + for (String value : values) { + try { + double parsedValue = Double.parseDouble(value.trim()); + if (parsedValue > 0) { + return parsedValue * DELAY_MILLISECONDS; + } + } catch (NumberFormatException e) { + // Continue to the next value + } } - return retryDelay; + return -1; } double tryParseDateHeader(String retryAfterHeader) { diff --git a/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java new file mode 100644 index 000000000..41ad313d8 --- /dev/null +++ b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java @@ -0,0 +1,61 @@ +package com.microsoft.kiota.http.middleware; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class RetryHandlerTest { + + private static final double DELAY_MILLISECONDS = 1000.0; + private static final double DEFAULT_DELAY_MILLISECONDS = -1.0; + private static final double DELTA = 0.01; + + private RetryHandler retryHandler; + + @BeforeEach + void setUp() { + retryHandler = new RetryHandler(); + } + + private static Stream retryAfterHeaderValues() { + return Stream.of( + // Single values + Arguments.of("10", 10.0 * DELAY_MILLISECONDS), + Arguments.of("0", DEFAULT_DELAY_MILLISECONDS), // Zero should return default + Arguments.of("-5", DEFAULT_DELAY_MILLISECONDS), // Negative should return default + + // Comma-separated values + Arguments.of("31,120", 31.0 * DELAY_MILLISECONDS), // First positive value + Arguments.of("0,45", 45.0 * DELAY_MILLISECONDS), // Skip zero, take next + Arguments.of("-10,25", 25.0 * DELAY_MILLISECONDS), // Skip negative, take next + Arguments.of("0,-5,60", 60.0 * DELAY_MILLISECONDS), // Skip multiple invalid + + // Edge cases + Arguments.of("", DEFAULT_DELAY_MILLISECONDS), // Empty string + Arguments.of(" ", DEFAULT_DELAY_MILLISECONDS), // Whitespace only + Arguments.of("abc", DEFAULT_DELAY_MILLISECONDS), // Invalid number + Arguments.of("10,abc,20", 10.0 * DELAY_MILLISECONDS), // Mixed valid/invalid + Arguments.of("abc,20", 20.0 * DELAY_MILLISECONDS), // Invalid first, valid second + Arguments.of("0,0,0", DEFAULT_DELAY_MILLISECONDS), // All zeros + Arguments.of("-1,-2,-3", DEFAULT_DELAY_MILLISECONDS), // All negative + + // Whitespace handling + Arguments.of(" 15 ", 15.0 * DELAY_MILLISECONDS), // Whitespace around value + Arguments.of("10, 20", 10.0 * DELAY_MILLISECONDS), // Whitespace after comma + Arguments.of(" 0 , 30 ", 30.0 * DELAY_MILLISECONDS) // Multiple whitespaces + ); + } + + @ParameterizedTest + @MethodSource("retryAfterHeaderValues") + void testTryParseTimeHeader(String headerValue, double expectedDelay) { + double result = retryHandler.tryParseTimeHeader(headerValue); + assertEquals( + expectedDelay, result, DELTA, "Failed for header value: '" + headerValue + "'"); + } +}